diff options
853 files changed, 27798 insertions, 78560 deletions
diff --git a/Android.bp b/Android.bp index c47b74717dcd..949373317097 100644 --- a/Android.bp +++ b/Android.bp @@ -916,6 +916,7 @@ java_library_host { ":ipconnectivity-proto-src", ":libstats_atom_enum_protos", ":libstats_internal_protos", + ":statsd_internal_protos", "cmds/am/proto/instrumentation_data.proto", "cmds/statsd/src/**/*.proto", "core/proto/**/*.proto", diff --git a/apex/Android.bp b/apex/Android.bp index 0a535a8fe9b9..4e80acb64e05 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -15,6 +15,7 @@ mainline_stubs_args = "--error UnhiddenSystemApi " + "--hide BroadcastBehavior " + + "--hide CallbackInterface " + "--hide DeprecationMismatch " + "--hide HiddenSuperclass " + "--hide HiddenTypedefConstant " + diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index 2be873cc8bca..380c64679fab 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -1,6 +1,27 @@ // Signature format: 2.0 package android.app.appsearch { + public final class AppSearchBatchResult<KeyType, ValueType> { + method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getFailures(); + method @NonNull public java.util.Map<KeyType,ValueType> getSuccesses(); + method public boolean isSuccess(); + } + + public final class AppSearchResult<ValueType> { + method @Nullable public String getErrorMessage(); + method public int getResultCode(); + method @Nullable public ValueType getResultValue(); + method public boolean isSuccess(); + field public static final int RESULT_INTERNAL_ERROR = 2; // 0x2 + field public static final int RESULT_INVALID_ARGUMENT = 3; // 0x3 + field public static final int RESULT_INVALID_SCHEMA = 7; // 0x7 + field public static final int RESULT_IO_ERROR = 4; // 0x4 + field public static final int RESULT_NOT_FOUND = 6; // 0x6 + field public static final int RESULT_OK = 0; // 0x0 + field public static final int RESULT_OUT_OF_SPACE = 5; // 0x5 + field public static final int RESULT_UNKNOWN_ERROR = 1; // 0x1 + } + public final class AppSearchSchema { method @NonNull public java.util.List<android.app.appsearch.AppSearchSchema.PropertyConfig> getProperties(); method @NonNull public String getSchemaType(); @@ -85,6 +106,43 @@ package android.app.appsearch { method @NonNull public BuilderType setTtlMillis(long); } + public final class GetByUriRequest { + method @NonNull public String getNamespace(); + method @NonNull public java.util.Set<java.lang.String> getUris(); + } + + public static final class GetByUriRequest.Builder { + ctor public GetByUriRequest.Builder(); + method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.lang.String...); + method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.GetByUriRequest build(); + method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String); + } + + public final class PutDocumentsRequest { + method @NonNull public java.util.List<android.app.appsearch.GenericDocument> getDocuments(); + } + + public static final class PutDocumentsRequest.Builder { + ctor public PutDocumentsRequest.Builder(); + method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull android.app.appsearch.GenericDocument...); + method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull java.util.Collection<android.app.appsearch.GenericDocument>); + method @NonNull public android.app.appsearch.PutDocumentsRequest build(); + } + + public final class RemoveByUriRequest { + method @NonNull public String getNamespace(); + method @NonNull public java.util.Set<java.lang.String> getUris(); + } + + public static final class RemoveByUriRequest.Builder { + ctor public RemoveByUriRequest.Builder(); + method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.lang.String...); + method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.RemoveByUriRequest build(); + method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String); + } + public final class SearchResult { method @NonNull public android.app.appsearch.GenericDocument getDocument(); method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches(); @@ -139,5 +197,18 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.SearchSpec.Builder setTermMatch(int); } + public final class SetSchemaRequest { + method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas(); + method public boolean isForceOverride(); + } + + public static final class SetSchemaRequest.Builder { + ctor public SetSchemaRequest.Builder(); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull android.app.appsearch.AppSearchSchema...); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>); + method @NonNull public android.app.appsearch.SetSchemaRequest build(); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean); + } + } diff --git a/apex/appsearch/framework/java/android/app/TEST_MAPPING b/apex/appsearch/framework/java/TEST_MAPPING index 12188f83a29f..12188f83a29f 100644 --- a/apex/appsearch/framework/java/android/app/TEST_MAPPING +++ b/apex/appsearch/framework/java/TEST_MAPPING diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java index 98daa66183a3..97cfe36fca80 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java @@ -33,7 +33,6 @@ import java.util.Map; * * @param <KeyType> The type of the keys for {@link #getSuccesses} and {@link #getFailures}. * @param <ValueType> The type of result objects associated with the keys. - * @hide */ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { @NonNull private final Map<KeyType, ValueType> mSuccesses; @@ -51,6 +50,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl mFailures = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null)); } + /** @hide */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeMap(mSuccesses); @@ -100,11 +100,14 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl return "{\n successes: " + mSuccesses + "\n failures: " + mFailures + "\n}"; } + /** @hide */ @Override public int describeContents() { return 0; } + /** @hide */ + @NonNull public static final Creator<AppSearchBatchResult> CREATOR = new Creator<AppSearchBatchResult>() { @NonNull diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index 5fd45eadbda9..442ca7b8639b 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -224,7 +224,11 @@ public class AppSearchManager { } AndroidFuture<AppSearchResult> future = new AndroidFuture<>(); try { - mService.setSchema(DEFAULT_DATABASE_NAME, schemaBundles, request.isForceOverride(), + mService.setSchema( + DEFAULT_DATABASE_NAME, + schemaBundles, + new ArrayList<>(request.getSchemasNotPlatformSurfaceable()), + request.isForceOverride(), new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { future.complete(result); diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java index 6e2ed70cca01..76225e40c56e 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java @@ -32,7 +32,6 @@ import java.util.Objects; * Information about the success or failure of an AppSearch call. * * @param <ValueType> The type of result object for successful calls. - * @hide */ public final class AppSearchResult<ValueType> implements Parcelable { /** @@ -107,6 +106,7 @@ public final class AppSearchResult<ValueType> implements Parcelable { mErrorMessage = in.readString(); } + /** @hide */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mResultCode); @@ -181,13 +181,15 @@ public final class AppSearchResult<ValueType> implements Parcelable { return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage; } + /** @hide */ @Override public int describeContents() { return 0; } - public static final Creator<AppSearchResult> CREATOR = - new Creator<AppSearchResult>() { + /** @hide */ + @NonNull + public static final Creator<AppSearchResult> CREATOR = new Creator<AppSearchResult>() { @NonNull @Override public AppSearchResult createFromParcel(@NonNull Parcel in) { diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 9c7ccea4c43b..b7cd4f5f8bce 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -140,7 +140,11 @@ public final class AppSearchSession { schemaBundles.add(schema.getBundle()); } try { - mService.setSchema(mDatabaseName, schemaBundles, request.isForceOverride(), + mService.setSchema( + mDatabaseName, + schemaBundles, + new ArrayList<>(request.getSchemasNotPlatformSurfaceable()), + request.isForceOverride(), new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { executor.execute(() -> callback.accept(result)); diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index 22e00f2cdfdc..1d7cb87131c0 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -32,6 +32,8 @@ interface IAppSearchManager { * * @param databaseName The databaseName this document resides in. * @param schemaBundles List of AppSearchSchema bundles. + * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform + * surfaces. * @param forceOverride Whether to apply the new schema even if it is incompatible. All * incompatible documents will be deleted. * @param callback {@link IAppSearchResultCallback#onResult} will be called with an @@ -40,6 +42,7 @@ interface IAppSearchManager { void setSchema( in String databaseName, in List<Bundle> schemaBundles, + in List<String> schemasNotPlatformSurfaceable, boolean forceOverride, in IAppSearchResultCallback callback); diff --git a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java deleted file mode 100644 index 3e472fd01939..000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.appsearch; - -import android.annotation.NonNull; -import android.annotation.SuppressLint; -import android.app.appsearch.exceptions.AppSearchException; -import android.util.ArraySet; - -import com.android.internal.util.Preconditions; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -/** - * Encapsulates a request to update the schema of an {@link AppSearchSession} database. - * - * @see AppSearchSession#setSchema - * @hide - */ -public final class SetSchemaRequest { - private final Set<AppSearchSchema> mSchemas; - private final boolean mForceOverride; - - SetSchemaRequest(Set<AppSearchSchema> schemas, boolean forceOverride) { - mSchemas = schemas; - mForceOverride = forceOverride; - } - - /** Returns the schemas that are part of this request. */ - @NonNull - public Set<AppSearchSchema> getSchemas() { - return mSchemas; - } - - /** Returns whether this request will force the schema to be overridden. */ - public boolean isForceOverride() { - return mForceOverride; - } - - /** Builder for {@link SetSchemaRequest} objects. */ - public static final class Builder { - private final Set<AppSearchSchema> mSchemas = new ArraySet<>(); - private boolean mForceOverride = false; - private boolean mBuilt = false; - - /** Adds one or more types to the schema. */ - @NonNull - public Builder addSchema(@NonNull AppSearchSchema... schemas) { - Preconditions.checkNotNull(schemas); - return addSchema(Arrays.asList(schemas)); - } - - /** Adds one or more types to the schema. */ - @NonNull - public Builder addSchema(@NonNull Collection<AppSearchSchema> schemas) { - Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(schemas); - mSchemas.addAll(schemas); - return this; - } - - /** - * Configures the {@link SetSchemaRequest} to delete any existing documents that don't - * follow the new schema. - * - * <p>By default, this is {@code false} and schema incompatibility causes the {@link - * AppSearchSession#setSchema} call to fail. - * - * @see AppSearchSession#setSchema - */ - @NonNull - public Builder setForceOverride(boolean forceOverride) { - mForceOverride = forceOverride; - return this; - } - - /** Builds a new {@link SetSchemaRequest}. */ - @NonNull - public SetSchemaRequest build() { - Preconditions.checkState(!mBuilt, "Builder has already been used"); - mBuilt = true; - return new SetSchemaRequest(mSchemas, mForceOverride); - } - } -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java index 9ca363ed9008..9ca363ed9008 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java index 2db74a8fb798..62cf38bca44d 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.appsearch.exceptions.IllegalSchemaException; +import android.app.appsearch.util.BundleUtil; import android.os.Bundle; import android.util.ArraySet; @@ -31,6 +32,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.Set; /** @@ -40,7 +42,7 @@ import java.util.Set; * * <p>The schema consists of type information, properties, and config (like tokenization type). * - * @see AppSearchManager#setSchema + * @see AppSearchSession#setSchema */ public final class AppSearchSchema { private static final String SCHEMA_TYPE_FIELD = "schemaType"; @@ -94,17 +96,37 @@ public final class AppSearchSchema { return ret; } + @Override + public boolean equals(@Nullable Object other) { + if (this == other) { + return true; + } + if (!(other instanceof AppSearchSchema)) { + return false; + } + AppSearchSchema otherSchema = (AppSearchSchema) other; + if (!getSchemaType().equals(otherSchema.getSchemaType())) { + return false; + } + return getProperties().equals(otherSchema.getProperties()); + } + + @Override + public int hashCode() { + return Objects.hash(getSchemaType(), getProperties()); + } + /** Builder for {@link AppSearchSchema objects}. */ public static final class Builder { - private final String mTypeName; + private final String mSchemaType; private final ArrayList<Bundle> mPropertyBundles = new ArrayList<>(); private final Set<String> mPropertyNames = new ArraySet<>(); private boolean mBuilt = false; /** Creates a new {@link AppSearchSchema.Builder}. */ - public Builder(@NonNull String typeName) { - Preconditions.checkNotNull(typeName); - mTypeName = typeName; + public Builder(@NonNull String schemaType) { + Preconditions.checkNotNull(schemaType); + mSchemaType = schemaType; } /** Adds a property to the given type. */ @@ -133,7 +155,7 @@ public final class AppSearchSchema { public AppSearchSchema build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); Bundle bundle = new Bundle(); - bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mTypeName); + bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mSchemaType); bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mPropertyBundles); mBuilt = true; return new AppSearchSchema(bundle); @@ -279,6 +301,8 @@ public final class AppSearchSchema { final Bundle mBundle; + @Nullable private Integer mHashCode; + PropertyConfig(@NonNull Bundle bundle) { mBundle = Preconditions.checkNotNull(bundle); } @@ -327,6 +351,26 @@ public final class AppSearchSchema { return mBundle.getInt(TOKENIZER_TYPE_FIELD); } + @Override + public boolean equals(@Nullable Object other) { + if (this == other) { + return true; + } + if (!(other instanceof PropertyConfig)) { + return false; + } + PropertyConfig otherProperty = (PropertyConfig) other; + return BundleUtil.deepEquals(this.mBundle, otherProperty.mBundle); + } + + @Override + public int hashCode() { + if (mHashCode == null) { + mHashCode = BundleUtil.deepHashCode(mBundle); + } + return mHashCode; + } + /** * Builder for {@link PropertyConfig}. * diff --git a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java index 0056377f007a..85207f7ed9d7 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.appsearch.exceptions.AppSearchException; +import android.app.appsearch.util.BundleUtil; import android.os.Bundle; import android.util.Log; @@ -37,12 +38,12 @@ import java.util.Set; * * <p>Documents are constructed via {@link GenericDocument.Builder}. * - * @see AppSearchManager#putDocuments - * @see AppSearchManager#getByUri - * @see AppSearchManager#query + * @see AppSearchSession#putDocuments + * @see AppSearchSession#getByUri + * @see AppSearchSession#query */ public class GenericDocument { - private static final String TAG = "GenericDocument"; + private static final String TAG = "AppSearchGenericDocumen"; /** The default empty namespace. */ public static final String DEFAULT_NAMESPACE = ""; @@ -462,144 +463,17 @@ public class GenericDocument { return false; } GenericDocument otherDocument = (GenericDocument) other; - return bundleEquals(this.mBundle, otherDocument.mBundle); - } - - /** - * Deeply checks whether two bundles are equal. - * - * <p>Two bundles will be considered equal if they contain the same content. - */ - @SuppressWarnings("unchecked") - private static boolean bundleEquals(Bundle one, Bundle two) { - if (one.size() != two.size()) { - return false; - } - Set<String> keySetOne = one.keySet(); - Object valueOne; - Object valueTwo; - // Bundle inherit its equals() from Object.java, which only compare their memory address. - // We should iterate all keys and check their presents and values in both bundle. - for (String key : keySetOne) { - valueOne = one.get(key); - valueTwo = two.get(key); - if (valueOne instanceof Bundle - && valueTwo instanceof Bundle - && !bundleEquals((Bundle) valueOne, (Bundle) valueTwo)) { - return false; - } else if (valueOne == null && (valueTwo != null || !two.containsKey(key))) { - // If we call bundle.get(key) when the 'key' doesn't actually exist in the - // bundle, we'll get back a null. So make sure that both values are null and - // both keys exist in the bundle. - return false; - } else if (valueOne instanceof boolean[]) { - if (!(valueTwo instanceof boolean[]) - || !Arrays.equals((boolean[]) valueOne, (boolean[]) valueTwo)) { - return false; - } - } else if (valueOne instanceof long[]) { - if (!(valueTwo instanceof long[]) - || !Arrays.equals((long[]) valueOne, (long[]) valueTwo)) { - return false; - } - } else if (valueOne instanceof double[]) { - if (!(valueTwo instanceof double[]) - || !Arrays.equals((double[]) valueOne, (double[]) valueTwo)) { - return false; - } - } else if (valueOne instanceof Bundle[]) { - if (!(valueTwo instanceof Bundle[])) { - return false; - } - Bundle[] bundlesOne = (Bundle[]) valueOne; - Bundle[] bundlesTwo = (Bundle[]) valueTwo; - if (bundlesOne.length != bundlesTwo.length) { - return false; - } - for (int i = 0; i < bundlesOne.length; i++) { - if (!bundleEquals(bundlesOne[i], bundlesTwo[i])) { - return false; - } - } - } else if (valueOne instanceof ArrayList) { - if (!(valueTwo instanceof ArrayList)) { - return false; - } - ArrayList<Bundle> bundlesOne = (ArrayList<Bundle>) valueOne; - ArrayList<Bundle> bundlesTwo = (ArrayList<Bundle>) valueTwo; - if (bundlesOne.size() != bundlesTwo.size()) { - return false; - } - for (int i = 0; i < bundlesOne.size(); i++) { - if (!bundleEquals(bundlesOne.get(i), bundlesTwo.get(i))) { - return false; - } - } - } else if (valueOne instanceof Object[]) { - if (!(valueTwo instanceof Object[]) - || !Arrays.equals((Object[]) valueOne, (Object[]) valueTwo)) { - return false; - } - } - } - return true; + return BundleUtil.deepEquals(this.mBundle, otherDocument.mBundle); } @Override public int hashCode() { if (mHashCode == null) { - mHashCode = bundleHashCode(mBundle); + mHashCode = BundleUtil.deepHashCode(mBundle); } return mHashCode; } - /** - * Calculates the hash code for a bundle. - * - * <p>The hash code is only effected by the contents in the bundle. Bundles will get consistent - * hash code if they have same contents. - */ - @SuppressWarnings("unchecked") - private static int bundleHashCode(Bundle bundle) { - int[] hashCodes = new int[bundle.size()]; - int i = 0; - // Bundle inherit its hashCode() from Object.java, which only relative to their memory - // address. Bundle doesn't have an order, so we should iterate all keys and combine - // their value's hashcode into an array. And use the hashcode of the array to be - // the hashcode of the bundle. - for (String key : bundle.keySet()) { - Object value = bundle.get(key); - if (value instanceof boolean[]) { - hashCodes[i++] = Arrays.hashCode((boolean[]) value); - } else if (value instanceof long[]) { - hashCodes[i++] = Arrays.hashCode((long[]) value); - } else if (value instanceof double[]) { - hashCodes[i++] = Arrays.hashCode((double[]) value); - } else if (value instanceof String[]) { - hashCodes[i++] = Arrays.hashCode((Object[]) value); - } else if (value instanceof Bundle) { - hashCodes[i++] = bundleHashCode((Bundle) value); - } else if (value instanceof Bundle[]) { - Bundle[] bundles = (Bundle[]) value; - int[] innerHashCodes = new int[bundles.length]; - for (int j = 0; j < innerHashCodes.length; j++) { - innerHashCodes[j] = bundleHashCode(bundles[j]); - } - hashCodes[i++] = Arrays.hashCode(innerHashCodes); - } else if (value instanceof ArrayList) { - ArrayList<Bundle> bundles = (ArrayList<Bundle>) value; - int[] innerHashCodes = new int[bundles.size()]; - for (int j = 0; j < innerHashCodes.length; j++) { - innerHashCodes[j] = bundleHashCode(bundles.get(j)); - } - hashCodes[i++] = Arrays.hashCode(innerHashCodes); - } else { - hashCodes[i++] = value.hashCode(); - } - } - return Arrays.hashCode(hashCodes); - } - @Override @NonNull public String toString() { @@ -683,10 +557,10 @@ public class GenericDocument { * * @param uri The uri of {@link GenericDocument}. * @param schemaType The schema type of the {@link GenericDocument}. The passed-in {@code - * schemaType} must be defined using {@link AppSearchManager#setSchema} prior to + * schemaType} must be defined using {@link AppSearchSession#setSchema} prior to * inserting a document of this {@code schemaType} into the AppSearch index using {@link - * AppSearchManager#putDocuments}. Otherwise, the document will be rejected by {@link - * AppSearchManager#putDocuments}. + * AppSearchSession#putDocuments}. Otherwise, the document will be rejected by {@link + * AppSearchSession#putDocuments}. */ @SuppressWarnings("unchecked") public Builder(@NonNull String uri, @NonNull String schemaType) { diff --git a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java index 053d401d06dc..74afdd2c7a80 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java @@ -30,7 +30,6 @@ import java.util.Set; * Encapsulates a request to retrieve documents by namespace and URI. * * @see AppSearchSession#getByUri - * @hide */ public final class GetByUriRequest { private final String mNamespace; diff --git a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java index 42f1ff2a716a..1c360a65a041 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java @@ -32,7 +32,6 @@ import java.util.List; * Encapsulates a request to index a document into an {@link AppSearchSession} database. * * @see AppSearchSession#putDocuments - * @hide */ public final class PutDocumentsRequest { private final List<GenericDocument> mDocuments; diff --git a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java index 3d83c390fbad..be6d15708d2e 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java @@ -30,7 +30,6 @@ import java.util.Set; * Encapsulates a request to remove documents by namespace and URI. * * @see AppSearchSession#removeByUri - * @hide */ public final class RemoveByUriRequest { private final String mNamespace; diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java index 5ffa7c94087c..5ffa7c94087c 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java index dbd09d6bb91b..dbd09d6bb91b 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java index 68e31f03fb4d..68e31f03fb4d 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java new file mode 100644 index 000000000000..0e031312f420 --- /dev/null +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java @@ -0,0 +1,179 @@ +/* + * 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.appsearch; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.app.appsearch.exceptions.AppSearchException; +import android.util.ArraySet; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Encapsulates a request to update the schema of an {@link AppSearchSession} database. + * + * @see AppSearchSession#setSchema + */ +public final class SetSchemaRequest { + private final Set<AppSearchSchema> mSchemas; + private final Set<String> mSchemasNotPlatformSurfaceable; + private final boolean mForceOverride; + + SetSchemaRequest( + @NonNull Set<AppSearchSchema> schemas, + @NonNull Set<String> schemasNotPlatformSurfaceable, + boolean forceOverride) { + mSchemas = Preconditions.checkNotNull(schemas); + mSchemasNotPlatformSurfaceable = Preconditions.checkNotNull(schemasNotPlatformSurfaceable); + mForceOverride = forceOverride; + } + + /** Returns the schemas that are part of this request. */ + @NonNull + public Set<AppSearchSchema> getSchemas() { + return Collections.unmodifiableSet(mSchemas); + } + + /** + * Returns the set of schema types that have opted out of being visible on system UI surfaces. + * + * @hide + */ + @NonNull + public Set<String> getSchemasNotPlatformSurfaceable() { + return Collections.unmodifiableSet(mSchemasNotPlatformSurfaceable); + } + + /** Returns whether this request will force the schema to be overridden. */ + public boolean isForceOverride() { + return mForceOverride; + } + + /** Builder for {@link SetSchemaRequest} objects. */ + public static final class Builder { + private final Set<AppSearchSchema> mSchemas = new ArraySet<>(); + private final Set<String> mSchemasNotPlatformSurfaceable = new ArraySet<>(); + private boolean mForceOverride = false; + private boolean mBuilt = false; + + /** + * Adds one or more types to the schema. + * + * <p>Any documents of these types will be visible on system UI surfaces by default. + */ + @NonNull + public Builder addSchema(@NonNull AppSearchSchema... schemas) { + Preconditions.checkNotNull(schemas); + return addSchema(Arrays.asList(schemas)); + } + + /** + * Adds one or more types to the schema. + * + * <p>Any documents of these types will be visible on system UI surfaces by default. + */ + @NonNull + public Builder addSchema(@NonNull Collection<AppSearchSchema> schemas) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(schemas); + mSchemas.addAll(schemas); + return this; + } + + /** + * Sets visibility on system UI surfaces for schema types. + * + * @hide + */ + @NonNull + public Builder setSchemaTypeVisibilityForSystemUi( + boolean visible, @NonNull String... schemaTypes) { + Preconditions.checkNotNull(schemaTypes); + return this.setSchemaTypeVisibilityForSystemUi(visible, Arrays.asList(schemaTypes)); + } + + /** + * Sets visibility on system UI surfaces for schema types. + * + * @hide + */ + @NonNull + public Builder setSchemaTypeVisibilityForSystemUi( + boolean visible, @NonNull Collection<String> schemaTypes) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(schemaTypes); + if (visible) { + mSchemasNotPlatformSurfaceable.removeAll(schemaTypes); + } else { + mSchemasNotPlatformSurfaceable.addAll(schemaTypes); + } + return this; + } + + /** + * Configures the {@link SetSchemaRequest} to delete any existing documents that don't + * follow the new schema. + * + * <p>By default, this is {@code false} and schema incompatibility causes the {@link + * AppSearchSession#setSchema} call to fail. + * + * @see AppSearchSession#setSchema + */ + @NonNull + public Builder setForceOverride(boolean forceOverride) { + mForceOverride = forceOverride; + return this; + } + + /** + * Builds a new {@link SetSchemaRequest}. + * + * @throws IllegalArgumentException If schema types were referenced, but the corresponding + * {@link AppSearchSchema} was never added. + */ + @NonNull + public SetSchemaRequest build() { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBuilt = true; + + // Verify that any schema types with visibility settings refer to a real schema. + // Create a copy because we're going to remove from the set for verification purposes. + Set<String> schemasNotPlatformSurfaceableCopy = + new ArraySet<>(mSchemasNotPlatformSurfaceable); + for (AppSearchSchema schema : mSchemas) { + schemasNotPlatformSurfaceableCopy.remove(schema.getSchemaType()); + } + if (!schemasNotPlatformSurfaceableCopy.isEmpty()) { + // We still have schema types that weren't seen in our mSchemas set. This means + // there wasn't a corresponding AppSearchSchema. + throw new IllegalArgumentException( + "Schema types " + + schemasNotPlatformSurfaceableCopy + + " referenced, but were not added."); + } + + return new SetSchemaRequest(mSchemas, mSchemasNotPlatformSurfaceable, mForceOverride); + } + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java index 704f180bffc4..704f180bffc4 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSchemaException.java index 5f8da7fe067a..5f8da7fe067a 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSchemaException.java diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSearchSpecException.java index 0b5dc2ece6f1..0b5dc2ece6f1 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSearchSpecException.java diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java new file mode 100644 index 000000000000..1b4d28401ea0 --- /dev/null +++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java @@ -0,0 +1,221 @@ +/* + * 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.appsearch.util; + +import android.annotation.Nullable; +import android.os.Bundle; +import android.util.SparseArray; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Utilities for working with {@link android.os.Bundle}. + * + * @hide + */ +public final class BundleUtil { + private BundleUtil() {} + + /** + * Deeply checks two bundles are equal or not. + * + * <p>Two bundles will be considered equal if they contain the same keys, and each value is also + * equal. Bundle values are compared using deepEquals. + */ + public static boolean deepEquals(@Nullable Bundle one, @Nullable Bundle two) { + if (one == null && two == null) { + return true; + } + if (one == null || two == null) { + return false; + } + if (one.size() != two.size()) { + return false; + } + if (!one.keySet().equals(two.keySet())) { + return false; + } + // Bundle inherit its equals() from Object.java, which only compare their memory address. + // We should iterate all keys and check their presents and values in both bundle. + for (String key : one.keySet()) { + if (!bundleValueEquals(one.get(key), two.get(key))) { + return false; + } + } + return true; + } + + /** + * Deeply checks whether two values in a Bundle are equal or not. + * + * <p>Values of type Bundle are compared using {@link #deepEquals}. + */ + private static boolean bundleValueEquals(@Nullable Object one, @Nullable Object two) { + if (one == null && two == null) { + return true; + } + if (one == null || two == null) { + return false; + } + if (one.equals(two)) { + return true; + } + if (one instanceof Bundle && two instanceof Bundle) { + return deepEquals((Bundle) one, (Bundle) two); + } else if (one instanceof int[] && two instanceof int[]) { + return Arrays.equals((int[]) one, (int[]) two); + } else if (one instanceof byte[] && two instanceof byte[]) { + return Arrays.equals((byte[]) one, (byte[]) two); + } else if (one instanceof char[] && two instanceof char[]) { + return Arrays.equals((char[]) one, (char[]) two); + } else if (one instanceof long[] && two instanceof long[]) { + return Arrays.equals((long[]) one, (long[]) two); + } else if (one instanceof float[] && two instanceof float[]) { + return Arrays.equals((float[]) one, (float[]) two); + } else if (one instanceof short[] && two instanceof short[]) { + return Arrays.equals((short[]) one, (short[]) two); + } else if (one instanceof double[] && two instanceof double[]) { + return Arrays.equals((double[]) one, (double[]) two); + } else if (one instanceof boolean[] && two instanceof boolean[]) { + return Arrays.equals((boolean[]) one, (boolean[]) two); + } else if (one instanceof Object[] && two instanceof Object[]) { + Object[] arrayOne = (Object[]) one; + Object[] arrayTwo = (Object[]) two; + if (arrayOne.length != arrayTwo.length) { + return false; + } + if (Arrays.equals(arrayOne, arrayTwo)) { + return true; + } + for (int i = 0; i < arrayOne.length; i++) { + if (!bundleValueEquals(arrayOne[i], arrayTwo[i])) { + return false; + } + } + return true; + } else if (one instanceof ArrayList && two instanceof ArrayList) { + ArrayList<?> listOne = (ArrayList<?>) one; + ArrayList<?> listTwo = (ArrayList<?>) two; + if (listOne.size() != listTwo.size()) { + return false; + } + for (int i = 0; i < listOne.size(); i++) { + if (!bundleValueEquals(listOne.get(i), listTwo.get(i))) { + return false; + } + } + return true; + } else if (one instanceof SparseArray && two instanceof SparseArray) { + SparseArray<?> arrayOne = (SparseArray<?>) one; + SparseArray<?> arrayTwo = (SparseArray<?>) two; + if (arrayOne.size() != arrayTwo.size()) { + return false; + } + for (int i = 0; i < arrayOne.size(); i++) { + if (arrayOne.keyAt(i) != arrayTwo.keyAt(i) + || !bundleValueEquals(arrayOne.valueAt(i), arrayTwo.valueAt(i))) { + return false; + } + } + return true; + } + return false; + } + + /** + * Calculates the hash code for a bundle. + * + * <p>The hash code is only effected by the contents in the bundle. Bundles will get consistent + * hash code if they have same contents. + */ + public static int deepHashCode(@Nullable Bundle bundle) { + if (bundle == null) { + return 0; + } + int[] hashCodes = new int[bundle.size()]; + int i = 0; + // Bundle inherit its hashCode() from Object.java, which only relative to their memory + // address. Bundle doesn't have an order, so we should iterate all keys and combine + // their value's hashcode into an array. And use the hashcode of the array to be + // the hashcode of the bundle. + for (String key : bundle.keySet()) { + Object value = bundle.get(key); + if (value instanceof Bundle) { + hashCodes[i++] = deepHashCode((Bundle) value); + } else if (value instanceof int[]) { + hashCodes[i++] = Arrays.hashCode((int[]) value); + } else if (value instanceof byte[]) { + hashCodes[i++] = Arrays.hashCode((byte[]) value); + } else if (value instanceof char[]) { + hashCodes[i++] = Arrays.hashCode((char[]) value); + } else if (value instanceof long[]) { + hashCodes[i++] = Arrays.hashCode((long[]) value); + } else if (value instanceof float[]) { + hashCodes[i++] = Arrays.hashCode((float[]) value); + } else if (value instanceof short[]) { + hashCodes[i++] = Arrays.hashCode((short[]) value); + } else if (value instanceof double[]) { + hashCodes[i++] = Arrays.hashCode((double[]) value); + } else if (value instanceof boolean[]) { + hashCodes[i++] = Arrays.hashCode((boolean[]) value); + } else if (value instanceof String[]) { + // Optimization to avoid Object[] handler creating an inner array for common cases + hashCodes[i++] = Arrays.hashCode((String[]) value); + } else if (value instanceof Object[]) { + Object[] array = (Object[]) value; + int[] innerHashCodes = new int[array.length]; + for (int j = 0; j < array.length; j++) { + if (array[j] instanceof Bundle) { + innerHashCodes[j] = deepHashCode((Bundle) array[j]); + } else if (array[j] != null) { + innerHashCodes[j] = array[j].hashCode(); + } + } + hashCodes[i++] = Arrays.hashCode(innerHashCodes); + } else if (value instanceof ArrayList) { + ArrayList<?> list = (ArrayList<?>) value; + int[] innerHashCodes = new int[list.size()]; + for (int j = 0; j < innerHashCodes.length; j++) { + Object item = list.get(j); + if (item instanceof Bundle) { + innerHashCodes[j] = deepHashCode((Bundle) item); + } else if (item != null) { + innerHashCodes[j] = item.hashCode(); + } + } + hashCodes[i++] = Arrays.hashCode(innerHashCodes); + } else if (value instanceof SparseArray) { + SparseArray<?> array = (SparseArray<?>) value; + int[] innerHashCodes = new int[array.size() * 2]; + for (int j = 0; j < array.size(); j++) { + innerHashCodes[j * 2] = array.keyAt(j); + Object item = array.valueAt(j); + if (item instanceof Bundle) { + innerHashCodes[j * 2 + 1] = deepHashCode((Bundle) item); + } else if (item != null) { + innerHashCodes[j * 2 + 1] = item.hashCode(); + } + } + hashCodes[i++] = Arrays.hashCode(innerHashCodes); + } else { + hashCodes[i++] = value.hashCode(); + } + } + return Arrays.hashCode(hashCodes); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index d5146dd75c3b..551347c5c202 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -33,15 +33,14 @@ import android.os.Bundle; import android.os.ParcelableException; import android.os.RemoteException; import android.os.UserHandle; -import android.util.ArraySet; import android.util.Log; import com.android.internal.util.Preconditions; import com.android.server.SystemService; import com.android.server.appsearch.external.localstorage.AppSearchImpl; +import java.util.ArrayList; import java.util.List; -import java.util.Set; /** * TODO(b/142567528): add comments when implement this class @@ -64,6 +63,7 @@ public class AppSearchManagerService extends SystemService { public void setSchema( @NonNull String databaseName, @NonNull List<Bundle> schemaBundles, + @NonNull List<String> schemasNotPlatformSurfaceable, boolean forceOverride, @NonNull IAppSearchResultCallback callback) { Preconditions.checkNotNull(databaseName); @@ -73,13 +73,13 @@ public class AppSearchManagerService extends SystemService { int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - Set<AppSearchSchema> schemas = new ArraySet<>(schemaBundles.size()); + List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size()); for (int i = 0; i < schemaBundles.size(); i++) { schemas.add(new AppSearchSchema(schemaBundles.get(i))); } AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); - impl.setSchema(databaseName, schemas, forceOverride); + impl.setSchema(databaseName, schemas, schemasNotPlatformSurfaceable, forceOverride); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(/*result=*/ null)); } catch (Throwable t) { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java index 247089ba258b..62b81d066c8b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java @@ -60,6 +60,7 @@ import com.google.android.icing.proto.SetSchemaResultProto; import com.google.android.icing.proto.StatusProto; import java.io.File; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -226,13 +227,16 @@ public final class AppSearchImpl { * * @param databaseName The name of the database where this schema lives. * @param schemas Schemas to set for this app. + * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform + * surfaces. * @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents * which do not comply with the new schema will be deleted. * @throws AppSearchException on IcingSearchEngine error. */ public void setSchema( @NonNull String databaseName, - @NonNull Set<AppSearchSchema> schemas, + @NonNull List<AppSearchSchema> schemas, + @NonNull List<String> schemasNotPlatformSurfaceable, boolean forceOverride) throws AppSearchException { mReadWriteLock.writeLock().lock(); @@ -240,8 +244,9 @@ public final class AppSearchImpl { SchemaProto.Builder existingSchemaBuilder = getSchemaProtoLocked().toBuilder(); SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); - for (AppSearchSchema schema : schemas) { - SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema); + for (int i = 0; i < schemas.size(); i++) { + SchemaTypeConfigProto schemaTypeProto = + SchemaToProtoConverter.toSchemaTypeConfigProto(schemas.get(i)); newSchemaBuilder.addTypes(schemaTypeProto); } @@ -276,8 +281,16 @@ public final class AppSearchImpl { // Update derived data structures. mSchemaMapLocked.put(databaseName, rewrittenSchemaResults.mRewrittenQualifiedTypes); - mVisibilityStoreLocked.updateSchemas( - databaseName, rewrittenSchemaResults.mDeletedQualifiedTypes); + + String databasePrefix = getDatabasePrefix(databaseName); + Set<String> qualifiedSchemasNotPlatformSurfaceable = + new ArraySet<>(schemasNotPlatformSurfaceable.size()); + for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) { + qualifiedSchemasNotPlatformSurfaceable.add( + databasePrefix + schemasNotPlatformSurfaceable.get(i)); + } + mVisibilityStoreLocked.setVisibility( + databaseName, qualifiedSchemasNotPlatformSurfaceable); // Determine whether to schedule an immediate optimize. if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0 @@ -294,38 +307,55 @@ public final class AppSearchImpl { } /** - * Update the visibility settings for this app. + * Retrieves the AppSearch schema for this database. * - * <p>This method belongs to the mutate group + * <p>This method belongs to query group. * - * @param databaseName The name of the database where the visibility settings will apply. - * @param schemasHiddenFromPlatformSurfaces Schemas that should be hidden from platform surfaces - * @throws AppSearchException on IcingSearchEngine error + * @param databaseName The name of the database where this schema lives. + * @throws AppSearchException on IcingSearchEngine error. */ - public void setVisibility( - @NonNull String databaseName, @NonNull Set<String> schemasHiddenFromPlatformSurfaces) - throws AppSearchException { - mReadWriteLock.writeLock().lock(); + @NonNull + public List<AppSearchSchema> getSchema(@NonNull String databaseName) throws AppSearchException { + SchemaProto fullSchema; + mReadWriteLock.readLock().lock(); try { - String databasePrefix = getDatabasePrefix(databaseName); - Set<String> qualifiedSchemasHiddenFromPlatformSurface = - new ArraySet<>(schemasHiddenFromPlatformSurfaces.size()); - for (String schema : schemasHiddenFromPlatformSurfaces) { - Set<String> existingSchemas = mSchemaMapLocked.get(databaseName); - if (existingSchemas == null || !existingSchemas.contains(databasePrefix + schema)) { - throw new AppSearchException( - AppSearchResult.RESULT_NOT_FOUND, - "Unknown schema(s): " - + schemasHiddenFromPlatformSurfaces - + " provided during setVisibility."); + fullSchema = getSchemaProtoLocked(); + } finally { + mReadWriteLock.readLock().unlock(); + } + + List<AppSearchSchema> result = new ArrayList<>(); + for (int i = 0; i < fullSchema.getTypesCount(); i++) { + String typeDatabase = getDatabaseName(fullSchema.getTypes(i).getSchemaType()); + if (!databaseName.equals(typeDatabase)) { + continue; + } + // Rewrite SchemaProto.types.schema_type + SchemaTypeConfigProto.Builder typeConfigBuilder = fullSchema.getTypes(i).toBuilder(); + String newSchemaType = + typeConfigBuilder.getSchemaType().substring(databaseName.length() + 1); + typeConfigBuilder.setSchemaType(newSchemaType); + + // Rewrite SchemaProto.types.properties.schema_type + for (int propertyIdx = 0; + propertyIdx < typeConfigBuilder.getPropertiesCount(); + propertyIdx++) { + PropertyConfigProto.Builder propertyConfigBuilder = + typeConfigBuilder.getProperties(propertyIdx).toBuilder(); + if (!propertyConfigBuilder.getSchemaType().isEmpty()) { + String newPropertySchemaType = + propertyConfigBuilder + .getSchemaType() + .substring(databaseName.length() + 1); + propertyConfigBuilder.setSchemaType(newPropertySchemaType); + typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder); } - qualifiedSchemasHiddenFromPlatformSurface.add(databasePrefix + schema); } - mVisibilityStoreLocked.setVisibility( - databaseName, qualifiedSchemasHiddenFromPlatformSurface); - } finally { - mReadWriteLock.writeLock().lock(); + + AppSearchSchema schema = SchemaToProtoConverter.toAppSearchSchema(typeConfigBuilder); + result.add(schema); } + return result; } /** @@ -340,7 +370,7 @@ public final class AppSearchImpl { public void putDocument(@NonNull String databaseName, @NonNull GenericDocument document) throws AppSearchException { DocumentProto.Builder documentBuilder = - GenericDocumentToProtoConverter.convert(document).toBuilder(); + GenericDocumentToProtoConverter.toDocumentProto(document).toBuilder(); addPrefixToDocument(documentBuilder, getDatabasePrefix(databaseName)); PutResultProto putResultProto; @@ -384,7 +414,7 @@ public final class AppSearchImpl { DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder(); removeDatabasesFromDocument(documentBuilder); - return GenericDocumentToProtoConverter.convert(documentBuilder.build()); + return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build()); } /** @@ -969,7 +999,7 @@ public final class AppSearchImpl { resultsBuilder.setResults(i, resultBuilder); } } - return SearchResultToProtoConverter.convertToSearchResultPage(resultsBuilder); + return SearchResultToProtoConverter.toSearchResultPage(resultsBuilder); } @GuardedBy("mReadWriteLock") diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java index 47228221a1f5..0b68ebcbfd3f 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java @@ -53,19 +53,23 @@ import java.util.Set; class VisibilityStore { // Schema type for documents that hold AppSearch's metadata, e.g. visibility settings @VisibleForTesting static final String SCHEMA_TYPE = "Visibility"; + // Property that holds the list of platform-hidden schemas, as part of the visibility // settings. - @VisibleForTesting static final String PLATFORM_HIDDEN_PROPERTY = "platformHidden"; + @VisibleForTesting + static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable"; // Database name to prefix all visibility schemas and documents with. Special-cased to // minimize the chance of collision with a client-supplied database. + @VisibleForTesting static final String DATABASE_NAME = "$$__AppSearch__Database"; + // Namespace of documents that contain visibility settings private static final String NAMESPACE = "namespace"; private final AppSearchImpl mAppSearchImpl; // The map contains schemas that are platform-hidden for each database. All schemas in the map // have a database name prefix. - private final Map<String, Set<String>> mPlatformHiddenMap = new ArrayMap<>(); + private final Map<String, Set<String>> mNotPlatformSurfaceableMap = new ArrayMap<>(); /** * Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()} @@ -82,8 +86,8 @@ class VisibilityStore { * * <p>This is kept separate from the constructor because this will call methods on * AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example, - * {@link AppSearchImpl#setSchema} will call {@link #updateSchemas}. We need to have both - * AppSearchImpl and VisibilityStore fully initialized for this call flow to work. + * {@link AppSearchImpl#setSchema} will call {@link #setVisibility(String, Set)}. We need to + * have both AppSearchImpl and VisibilityStore fully initialized for this call flow to work. * * @throws AppSearchException AppSearchException on AppSearchImpl error. */ @@ -92,11 +96,11 @@ class VisibilityStore { // Schema type doesn't exist yet. Add it. mAppSearchImpl.setSchema( DATABASE_NAME, - Collections.singleton( + Collections.singletonList( new AppSearchSchema.Builder(SCHEMA_TYPE) .addProperty( new AppSearchSchema.PropertyConfig.Builder( - PLATFORM_HIDDEN_PROPERTY) + NOT_PLATFORM_SURFACEABLE_PROPERTY) .setDataType( AppSearchSchema.PropertyConfig .DATA_TYPE_STRING) @@ -105,6 +109,7 @@ class VisibilityStore { .CARDINALITY_REPEATED) .build()) .build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); } @@ -120,8 +125,9 @@ class VisibilityStore { GenericDocument document = mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ database); - String[] schemas = document.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY); - mPlatformHiddenMap.put(database, new ArraySet<>(Arrays.asList(schemas))); + String[] schemas = + document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY); + mNotPlatformSurfaceableMap.put(database, new ArraySet<>(Arrays.asList(schemas))); } catch (AppSearchException e) { if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) { // TODO(b/172068212): This indicates some desync error. We were expecting a @@ -136,94 +142,33 @@ class VisibilityStore { } /** - * Update visibility settings for the {@code databaseName}. - * - * @param schemasToRemove Database-prefixed schemas that should be removed - */ - public void updateSchemas(@NonNull String databaseName, @NonNull Set<String> schemasToRemove) - throws AppSearchException { - Preconditions.checkNotNull(databaseName); - Preconditions.checkNotNull(schemasToRemove); - - GenericDocument visibilityDocument; - try { - visibilityDocument = - mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ databaseName); - } catch (AppSearchException e) { - if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) { - // This might be the first time we're seeing visibility changes for a database. - // Create a new visibility document. - mAppSearchImpl.putDocument( - DATABASE_NAME, - new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE) - .setNamespace(NAMESPACE) - .build()); - - // Since we know there was nothing that existed before, we don't need to remove - // anything either. Return early. - return; - } - // Otherwise, this is some real error we should pass up. - throw e; - } - - String[] hiddenSchemas = - visibilityDocument.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY); - if (hiddenSchemas == null) { - // Nothing to remove. - return; - } - - // Create a new set so we can remove from it. - Set<String> remainingSchemas = new ArraySet<>(Arrays.asList(hiddenSchemas)); - boolean changed = remainingSchemas.removeAll(schemasToRemove); - if (!changed) { - // Nothing was actually removed. Can return early. - return; - } - - // Update our persisted document - // TODO(b/171882200): Switch to a .toBuilder API when it's available. - GenericDocument.Builder newVisibilityDocument = - new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE) - .setNamespace(NAMESPACE); - if (!remainingSchemas.isEmpty()) { - newVisibilityDocument.setPropertyString( - PLATFORM_HIDDEN_PROPERTY, remainingSchemas.toArray(new String[0])); - } - mAppSearchImpl.putDocument(DATABASE_NAME, newVisibilityDocument.build()); - - // Update derived data structures - mPlatformHiddenMap.put(databaseName, remainingSchemas); - } - - /** * Sets visibility settings for {@code databaseName}. Any previous visibility settings will be * overwritten. * - * @param databaseName Database name that owns the {@code platformHiddenSchemas}. - * @param platformHiddenSchemas Set of database-qualified schemas that should be hidden from the - * platform. + * @param databaseName Database name that owns the {@code schemasNotPlatformSurfaceable}. + * @param schemasNotPlatformSurfaceable Set of database-qualified schemas that should be hidden + * from the platform. * @throws AppSearchException on AppSearchImpl error. */ public void setVisibility( - @NonNull String databaseName, @NonNull Set<String> platformHiddenSchemas) + @NonNull String databaseName, @NonNull Set<String> schemasNotPlatformSurfaceable) throws AppSearchException { Preconditions.checkNotNull(databaseName); - Preconditions.checkNotNull(platformHiddenSchemas); + Preconditions.checkNotNull(schemasNotPlatformSurfaceable); // Persist the document GenericDocument.Builder visibilityDocument = new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE) .setNamespace(NAMESPACE); - if (!platformHiddenSchemas.isEmpty()) { + if (!schemasNotPlatformSurfaceable.isEmpty()) { visibilityDocument.setPropertyString( - PLATFORM_HIDDEN_PROPERTY, platformHiddenSchemas.toArray(new String[0])); + NOT_PLATFORM_SURFACEABLE_PROPERTY, + schemasNotPlatformSurfaceable.toArray(new String[0])); } mAppSearchImpl.putDocument(DATABASE_NAME, visibilityDocument.build()); // Update derived data structures. - mPlatformHiddenMap.put(databaseName, platformHiddenSchemas); + mNotPlatformSurfaceableMap.put(databaseName, schemasNotPlatformSurfaceable); } /** @@ -235,13 +180,13 @@ class VisibilityStore { * none exist. */ @NonNull - public Set<String> getPlatformHiddenSchemas(@NonNull String databaseName) { + public Set<String> getSchemasNotPlatformSurfaceable(@NonNull String databaseName) { Preconditions.checkNotNull(databaseName); - Set<String> platformHiddenSchemas = mPlatformHiddenMap.get(databaseName); - if (platformHiddenSchemas == null) { + Set<String> schemasNotPlatformSurfaceable = mNotPlatformSurfaceableMap.get(databaseName); + if (schemasNotPlatformSurfaceable == null) { return Collections.emptySet(); } - return platformHiddenSchemas; + return schemasNotPlatformSurfaceable; } /** @@ -251,7 +196,7 @@ class VisibilityStore { * @throws AppSearchException on AppSearchImpl error. */ public void handleReset() throws AppSearchException { - mPlatformHiddenMap.clear(); + mNotPlatformSurfaceableMap.clear(); initialize(); } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java index 8f4e7ff69d7c..5474cd04287c 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java @@ -39,7 +39,7 @@ public final class GenericDocumentToProtoConverter { /** Converts a {@link GenericDocument} into a {@link DocumentProto}. */ @NonNull @SuppressWarnings("unchecked") - public static DocumentProto convert(@NonNull GenericDocument document) { + public static DocumentProto toDocumentProto(@NonNull GenericDocument document) { Preconditions.checkNotNull(document); DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder(); mProtoBuilder @@ -82,7 +82,7 @@ public final class GenericDocumentToProtoConverter { } } else if (documentValues != null) { for (int j = 0; j < documentValues.length; j++) { - DocumentProto proto = convert(documentValues[j]); + DocumentProto proto = toDocumentProto(documentValues[j]); propertyProto.addDocumentValues(proto); } } else { @@ -96,7 +96,7 @@ public final class GenericDocumentToProtoConverter { /** Converts a {@link DocumentProto} into a {@link GenericDocument}. */ @NonNull - public static GenericDocument convert(@NonNull DocumentProto proto) { + public static GenericDocument toGenericDocument(@NonNull DocumentProto proto) { Preconditions.checkNotNull(proto); GenericDocument.Builder<?> documentBuilder = new GenericDocument.Builder<>(proto.getUri(), proto.getSchema()) @@ -141,7 +141,7 @@ public final class GenericDocumentToProtoConverter { } else if (property.getDocumentValuesCount() > 0) { GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()]; for (int j = 0; j < values.length; j++) { - values[j] = convert(property.getDocumentValues(j)); + values[j] = toGenericDocument(property.getDocumentValues(j)); } documentBuilder.setPropertyDocument(name, values); } else { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java index 642c2a713930..4165af31c00b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java @@ -18,11 +18,13 @@ package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; import android.app.appsearch.AppSearchSchema; +import android.util.Log; import com.android.internal.util.Preconditions; import com.google.android.icing.proto.PropertyConfigProto; import com.google.android.icing.proto.SchemaTypeConfigProto; +import com.google.android.icing.proto.SchemaTypeConfigProtoOrBuilder; import com.google.android.icing.proto.StringIndexingConfig; import com.google.android.icing.proto.TermMatchType; @@ -34,6 +36,8 @@ import java.util.List; * @hide */ public final class SchemaToProtoConverter { + private static final String TAG = "AppSearchSchemaToProtoC"; + private SchemaToProtoConverter() {} /** @@ -41,23 +45,23 @@ public final class SchemaToProtoConverter { * SchemaTypeConfigProto}. */ @NonNull - public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) { + public static SchemaTypeConfigProto toSchemaTypeConfigProto(@NonNull AppSearchSchema schema) { Preconditions.checkNotNull(schema); SchemaTypeConfigProto.Builder protoBuilder = SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaType()); List<AppSearchSchema.PropertyConfig> properties = schema.getProperties(); for (int i = 0; i < properties.size(); i++) { - PropertyConfigProto propertyProto = convertProperty(properties.get(i)); + PropertyConfigProto propertyProto = toPropertyConfigProto(properties.get(i)); protoBuilder.addProperties(propertyProto); } return protoBuilder.build(); } @NonNull - private static PropertyConfigProto convertProperty( + private static PropertyConfigProto toPropertyConfigProto( @NonNull AppSearchSchema.PropertyConfig property) { Preconditions.checkNotNull(property); - PropertyConfigProto.Builder propertyConfigProto = + PropertyConfigProto.Builder builder = PropertyConfigProto.newBuilder().setPropertyName(property.getName()); StringIndexingConfig.Builder indexingConfig = StringIndexingConfig.newBuilder(); @@ -68,12 +72,12 @@ public final class SchemaToProtoConverter { if (dataTypeProto == null) { throw new IllegalArgumentException("Invalid dataType: " + dataType); } - propertyConfigProto.setDataType(dataTypeProto); + builder.setDataType(dataTypeProto); // Set schemaType String schemaType = property.getSchemaType(); if (schemaType != null) { - propertyConfigProto.setSchemaType(schemaType); + builder.setSchemaType(schemaType); } // Set cardinality @@ -83,7 +87,7 @@ public final class SchemaToProtoConverter { if (cardinalityProto == null) { throw new IllegalArgumentException("Invalid cardinality: " + dataType); } - propertyConfigProto.setCardinality(cardinalityProto); + builder.setCardinality(cardinalityProto); // Set indexingType @AppSearchSchema.PropertyConfig.IndexingType int indexingType = property.getIndexingType(); @@ -114,7 +118,63 @@ public final class SchemaToProtoConverter { indexingConfig.setTokenizerType(tokenizerTypeProto); // Build! - propertyConfigProto.setStringIndexingConfig(indexingConfig); - return propertyConfigProto.build(); + builder.setStringIndexingConfig(indexingConfig); + return builder.build(); + } + + /** + * Converts a {@link SchemaTypeConfigProto} into an {@link + * android.app.appsearch.AppSearchSchema}. + */ + @NonNull + public static AppSearchSchema toAppSearchSchema(@NonNull SchemaTypeConfigProtoOrBuilder proto) { + Preconditions.checkNotNull(proto); + AppSearchSchema.Builder builder = new AppSearchSchema.Builder(proto.getSchemaType()); + List<PropertyConfigProto> properties = proto.getPropertiesList(); + for (int i = 0; i < properties.size(); i++) { + AppSearchSchema.PropertyConfig propertyConfig = toPropertyConfig(properties.get(i)); + builder.addProperty(propertyConfig); + } + return builder.build(); + } + + @NonNull + private static AppSearchSchema.PropertyConfig toPropertyConfig( + @NonNull PropertyConfigProto proto) { + Preconditions.checkNotNull(proto); + AppSearchSchema.PropertyConfig.Builder builder = + new AppSearchSchema.PropertyConfig.Builder(proto.getPropertyName()) + .setDataType(proto.getDataType().getNumber()) + .setCardinality(proto.getCardinality().getNumber()) + .setTokenizerType( + proto.getStringIndexingConfig().getTokenizerType().getNumber()); + + // Set schema + if (!proto.getSchemaType().isEmpty()) { + builder.setSchemaType(proto.getSchemaType()); + } + + // Set indexingType + @AppSearchSchema.PropertyConfig.IndexingType int indexingType; + TermMatchType.Code termMatchTypeProto = proto.getStringIndexingConfig().getTermMatchType(); + switch (termMatchTypeProto) { + case UNKNOWN: + indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE; + break; + case EXACT_ONLY: + indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS; + break; + case PREFIX: + indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES; + break; + default: + // Avoid crashing in the 'read' path; we should try to interpret the document to the + // extent possible. + Log.w(TAG, "Invalid indexingType: " + termMatchTypeProto.getNumber()); + indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE; + } + builder.setIndexingType(indexingType); + + return builder.build(); } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java index b91a393cab4a..4d107a970abd 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java @@ -39,13 +39,12 @@ public class SearchResultToProtoConverter { /** Translate a {@link SearchResultProto} into {@link SearchResultPage}. */ @NonNull - public static SearchResultPage convertToSearchResultPage( - @NonNull SearchResultProtoOrBuilder proto) { + public static SearchResultPage toSearchResultPage(@NonNull SearchResultProtoOrBuilder proto) { Bundle bundle = new Bundle(); bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken()); ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount()); for (int i = 0; i < proto.getResultsCount(); i++) { - resultBundles.add(convertToSearchResultBundle(proto.getResults(i))); + resultBundles.add(toSearchResultBundle(proto.getResults(i))); } bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); return new SearchResultPage(bundle); @@ -53,10 +52,11 @@ public class SearchResultToProtoConverter { /** Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. */ @NonNull - private static Bundle convertToSearchResultBundle( + private static Bundle toSearchResultBundle( @NonNull SearchResultProto.ResultProtoOrBuilder proto) { Bundle bundle = new Bundle(); - GenericDocument document = GenericDocumentToProtoConverter.convert(proto.getDocument()); + GenericDocument document = + GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument()); bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle()); ArrayList<Bundle> matchList = new ArrayList<>(); diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index a2bf0d504a1f..57c48d838808 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I2decd83fab4c4d58fe38c9970f804046479c942c +If5fd2bd705d5507d044706701a94b2e1496ef1df diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 3597c27224bb..d16217d5cb41 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -274,13 +274,20 @@ public class JobInfo implements Parcelable { /** * This job needs to be exempted from the app standby throttling. Only the system (UID 1000) - * can set it. Jobs with a time constrant must not have it. + * can set it. Jobs with a time constraint must not have it. * * @hide */ public static final int FLAG_EXEMPT_FROM_APP_STANDBY = 1 << 3; /** + * Whether it's a so-called "HPJ" or not. + * + * @hide + */ + public static final int FLAG_FOREGROUND_JOB = 1 << 4; + + /** * @hide */ public static final int CONSTRAINT_FLAG_CHARGING = 1 << 0; @@ -571,6 +578,7 @@ public class JobInfo implements Parcelable { /** * Return the backoff policy of this job. + * * @see JobInfo.Builder#setBackoffCriteria(long, int) */ public @BackoffPolicy int getBackoffPolicy() { @@ -578,6 +586,13 @@ public class JobInfo implements Parcelable { } /** + * @see JobInfo.Builder#setForeground(boolean) + */ + public boolean isForegroundJob() { + return (flags & FLAG_FOREGROUND_JOB) != 0; + } + + /** * @see JobInfo.Builder#setImportantWhileForeground(boolean) */ public boolean isImportantWhileForeground() { @@ -1442,6 +1457,41 @@ public class JobInfo implements Parcelable { } /** + * Setting this to true indicates that this job is important and needs to run as soon as + * possible with stronger guarantees than regular jobs. These "foreground" jobs will: + * <ol> + * <li>Run as soon as possible</li> + * <li>Be exempted from Doze and battery saver restrictions</li> + * <li>Have network access</li> + * </ol> + * + * Since these jobs have stronger guarantees than regular jobs, they will be subject to + * stricter quotas. As long as an app has available foreground quota, jobs scheduled with + * this set to true will run with these guarantees. If an app has run out of available + * foreground quota, any pending foreground jobs will run as regular jobs. + * {@link JobParameters#isForegroundJob()} can be used to know whether the executing job + * has foreground guarantees or not. In addition, {@link JobScheduler#schedule(JobInfo)} + * will immediately return {@link JobScheduler#RESULT_FAILURE} if the app does not have + * available quota (and the job will not be successfully scheduled). + * + * Foreground jobs may only set network constraints. No other constraints are allowed. + * + * Note: Even though foreground jobs are meant to run as soon as possible, they may be + * deferred if the system is under heavy load or the network constraint is satisfied + * + * @see JobInfo#isForegroundJob() + */ + @NonNull + public Builder setForeground(boolean foreground) { + if (foreground) { + mFlags |= FLAG_FOREGROUND_JOB; + } else { + mFlags &= (~FLAG_FOREGROUND_JOB); + } + return this; + } + + /** * Setting this to true indicates that this job is important while the scheduling app * is in the foreground or on the temporary whitelist for background restrictions. * This means that the system will relax doze restrictions on this job during this time. @@ -1456,7 +1506,9 @@ public class JobInfo implements Parcelable { * @param importantWhileForeground whether to relax doze restrictions for this job when the * app is in the foreground. False by default. * @see JobInfo#isImportantWhileForeground() + * @deprecated Use {@link #setForeground(boolean)} instead. */ + @Deprecated public Builder setImportantWhileForeground(boolean importantWhileForeground) { if (importantWhileForeground) { mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND; @@ -1580,6 +1632,29 @@ public class JobInfo implements Parcelable { throw new IllegalArgumentException( "An important while foreground job cannot have a time delay"); } + + if ((flags & FLAG_FOREGROUND_JOB) != 0) { + if (hasEarlyConstraint) { + throw new IllegalArgumentException("A foreground job cannot have a time delay"); + } + if (hasLateConstraint) { + throw new IllegalArgumentException("A foreground job cannot have a deadline"); + } + if (isPeriodic) { + throw new IllegalArgumentException("A foreground job cannot be periodic"); + } + if (isPersisted) { + throw new IllegalArgumentException("A foreground job cannot be persisted"); + } + if (constraintFlags != 0 || (flags & ~FLAG_FOREGROUND_JOB) != 0) { + throw new IllegalArgumentException( + "A foreground job can only have network constraints"); + } + if (triggerContentUris != null && triggerContentUris.length > 0) { + throw new IllegalArgumentException( + "Can't call addTriggerContentUri() on a foreground job"); + } + } } /** diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java index e251ff00a47d..a71b8562753e 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java @@ -111,6 +111,9 @@ public class JobParameters implements Parcelable { @UnsupportedAppUsage private final IBinder callback; private final boolean overrideDeadlineExpired; + // HPJs = foreground jobs. + // TODO(171305774): clean up naming + private final boolean mIsHpj; private final Uri[] mTriggeredContentUris; private final String[] mTriggeredContentAuthorities; private final Network network; @@ -121,7 +124,7 @@ public class JobParameters implements Parcelable { /** @hide */ public JobParameters(IBinder callback, int jobId, PersistableBundle extras, Bundle transientExtras, ClipData clipData, int clipGrantFlags, - boolean overrideDeadlineExpired, Uri[] triggeredContentUris, + boolean overrideDeadlineExpired, boolean isHpj, Uri[] triggeredContentUris, String[] triggeredContentAuthorities, Network network) { this.jobId = jobId; this.extras = extras; @@ -130,6 +133,7 @@ public class JobParameters implements Parcelable { this.clipGrantFlags = clipGrantFlags; this.callback = callback; this.overrideDeadlineExpired = overrideDeadlineExpired; + this.mIsHpj = isHpj; this.mTriggeredContentUris = triggeredContentUris; this.mTriggeredContentAuthorities = triggeredContentAuthorities; this.network = network; @@ -195,6 +199,17 @@ public class JobParameters implements Parcelable { } /** + * @return Whether this job is running as a foreground job or not. A job is guaranteed to have + * all foreground job guarantees for the duration of the job execution if this returns + * {@code true}. This will return {@code false} if the job that wasn't requested to run as a + * foreground job, or if it was requested to run as a foreground job but the app didn't have + * any remaining foreground job quota at the time of execution. + */ + public boolean isForegroundJob() { + return mIsHpj; + } + + /** * For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this * provides an easy way to tell whether the job is being executed due to the deadline * expiring. Note: If the job is running because its deadline expired, it implies that its @@ -337,6 +352,7 @@ public class JobParameters implements Parcelable { } callback = in.readStrongBinder(); overrideDeadlineExpired = in.readInt() == 1; + mIsHpj = in.readBoolean(); mTriggeredContentUris = in.createTypedArray(Uri.CREATOR); mTriggeredContentAuthorities = in.createStringArray(); if (in.readInt() != 0) { @@ -373,6 +389,7 @@ public class JobParameters implements Parcelable { } dest.writeStrongBinder(callback); dest.writeInt(overrideDeadlineExpired ? 1 : 0); + dest.writeBoolean(mIsHpj); dest.writeTypedArray(mTriggeredContentUris, flags); dest.writeStringArray(mTriggeredContentAuthorities); if (network != null) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java index b35a7be1f689..0feaade2b51d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -205,7 +205,9 @@ class JobConcurrencyManager { } private boolean isFgJob(JobStatus job) { - return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP; + // (It's super confusing PRIORITY_BOUND_FOREGROUND_SERVICE isn't FG here) + return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP + || job.shouldTreatAsForegroundJob(); } @GuardedBy("mLock") @@ -336,6 +338,8 @@ class JobConcurrencyManager { continue; } + // TODO(171305774): make sure HPJs aren't pre-empted and add dedicated contexts for them + final boolean isPendingFg = isFgJob(nextPending); // Find an available slot for nextPending. The context should be available OR diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 255e2f6d8779..9ea402c56088 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -834,6 +834,13 @@ public class JobSchedulerService extends com.android.server.SystemService // Higher override state (OVERRIDE_FULL) should be before lower state (OVERRIDE_SOFT) return o2.overrideState - o1.overrideState; } + if (o1.getSourceUid() == o2.getSourceUid()) { + final boolean o1FGJ = o1.isRequestedForegroundJob(); + if (o1FGJ != o2.isRequestedForegroundJob()) { + // Attempt to run requested HPJs ahead of regular jobs, regardless of HPJ quota. + return o1FGJ ? -1 : 1; + } + } if (o1.enqueueTime < o2.enqueueTime) { return -1; } @@ -1136,6 +1143,12 @@ public class JobSchedulerService extends com.android.server.SystemService JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); + // Return failure early if HPJ quota used up. + if (jobStatus.isRequestedForegroundJob() + && !mQuotaController.isWithinHpjQuotaLocked(jobStatus)) { + return JobScheduler.RESULT_FAILURE; + } + // Give exemption if the source is in the foreground just now. // Note if it's a sync job, this method is called on the handler so it's not exactly // the state when requestSync() was called, but that should be fine because of the @@ -1791,9 +1804,9 @@ public class JobSchedulerService extends com.android.server.SystemService * time of the job to be the time of completion (i.e. the time at which this function is * called). * <p>This could be inaccurate b/c the job can run for as long as - * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead - * to underscheduling at least, rather than if we had taken the last execution time to be the - * start of the execution. + * {@link com.android.server.job.JobServiceContext#DEFAULT_EXECUTING_TIMESLICE_MILLIS}, but + * will lead to underscheduling at least, rather than if we had taken the last execution time + * to be the start of the execution. * * @return A new job representing the execution criteria for this instantiation of the * recurring job. @@ -1884,6 +1897,10 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); } + // Intentionally not checking HPJ quota here. An app can't find out if it's run out of quota + // when it asks JS to reschedule an HPJ. Instead, the rescheduled HPJ will just be demoted + // to a regular job if the app has no HPJ quota left. + // If the job wants to be rescheduled, we first need to make the next upcoming // job so we can transfer any appropriate state over from the previous job when // we stop it. @@ -2382,7 +2399,7 @@ public class JobSchedulerService extends com.android.server.SystemService public long getMaxJobExecutionTimeMs(JobStatus job) { synchronized (mLock) { return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job), - JobServiceContext.EXECUTING_TIMESLICE_MILLIS); + JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS); } } @@ -2600,7 +2617,6 @@ public class JobSchedulerService extends com.android.server.SystemService // job that runs one of the app's services, as well as verifying that the // named service properly requires the BIND_JOB_SERVICE permission private void enforceValidJobRequest(int uid, JobInfo job) { - job.enforceValidity(); final PackageManager pm = getContext() .createContextAsUser(UserHandle.getUserHandleForUid(uid), 0) .getPackageManager(); @@ -2649,6 +2665,7 @@ public class JobSchedulerService extends com.android.server.SystemService } private void validateJobFlags(JobInfo job, int callingUid) { + job.enforceValidity(); if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { getContext().enforceCallingOrSelfPermission( android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 565ed959aeb4..5fed1995dfc3 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -16,6 +16,7 @@ package com.android.server.job; +import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.app.job.IJobCallback; @@ -73,7 +74,13 @@ public final class JobServiceContext implements ServiceConnection { private static final String TAG = "JobServiceContext"; /** Amount of time a job is allowed to execute for before being considered timed-out. */ - public static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins. + public static final long DEFAULT_EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins. + /** + * Amount of time a RESTRICTED HPJ is allowed to execute for before being considered + * timed-out. + */ + public static final long DEFAULT_RESTRICTED_HPJ_EXECUTING_TIMESLICE_MILLIS = + DEFAULT_EXECUTING_TIMESLICE_MILLIS / 2; /** Amount of time the JobScheduler waits for the initial service launch+bind. */ private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000; /** Amount of time the JobScheduler will wait for a response from an app for a message. */ @@ -224,7 +231,8 @@ public final class JobServiceContext implements ServiceConnection { final JobInfo ji = job.getJob(); mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(), ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(), - isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network); + isDeadlineExpired, job.shouldTreatAsForegroundJob(), + triggeredUris, triggeredAuthorities, job.network); mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis(); final long whenDeferred = job.getWhenStandbyDeferred(); @@ -250,9 +258,18 @@ public final class JobServiceContext implements ServiceConnection { final Intent intent = new Intent().setComponent(job.getServiceComponent()); boolean binding = false; try { - binding = mContext.bindServiceAsUser(intent, this, - Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND - | Context.BIND_NOT_PERCEPTIBLE, + final int bindFlags; + if (job.shouldTreatAsForegroundJob()) { + // Add BIND_FOREGROUND_SERVICE to make it BFGS. Without it, it'll be + // PROCESS_STATE_IMPORTANT_FOREGROUND. Unclear which is better here. + // TODO(171305774): The job should run on the little cores. We'll probably need + // another binding flag for that. + bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; + } else { + bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND + | Context.BIND_NOT_PERCEPTIBLE; + } + binding = mContext.bindServiceAsUser(intent, this, bindFlags, UserHandle.of(job.getUserId())); } catch (SecurityException e) { // Some permission policy, for example INTERACT_ACROSS_USERS and @@ -848,7 +865,10 @@ public final class JobServiceContext implements ServiceConnection { final long timeoutMillis; switch (mVerb) { case VERB_EXECUTING: - timeoutMillis = EXECUTING_TIMESLICE_MILLIS; + timeoutMillis = mRunningJob.shouldTreatAsForegroundJob() + && mRunningJob.getStandbyBucket() == RESTRICTED_INDEX + ? DEFAULT_RESTRICTED_HPJ_EXECUTING_TIMESLICE_MILLIS + : DEFAULT_EXECUTING_TIMESLICE_MILLIS; break; case VERB_BINDING: diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 8f6c68dad36c..5ec1b8951a7b 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -85,6 +85,7 @@ public final class JobStatus { static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26; static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint + static final int CONSTRAINT_WITHIN_HPJ_QUOTA = 1 << 23; // Implicit constraint static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint /** @@ -376,6 +377,9 @@ public final class JobStatus { /** The job is within its quota based on its standby bucket. */ private boolean mReadyWithinQuota; + /** The job is a foreground job with sufficient quota to run as a foreground job. */ + private boolean mReadyWithinHpjQuota; + /** The job's dynamic requirements have been satisfied. */ private boolean mReadyDynamicSatisfied; @@ -1036,20 +1040,34 @@ public final class JobStatus { mPersistedUtcTimes = null; } + /** @return true if the app has requested that this run as a foreground job. */ + public boolean isRequestedForegroundJob() { + return (getFlags() & JobInfo.FLAG_FOREGROUND_JOB) != 0; + } + + /** + * @return true if all foreground job requirements are satisfied and therefore this should be + * treated as a foreground job. + */ + public boolean shouldTreatAsForegroundJob() { + return mReadyWithinHpjQuota && isRequestedForegroundJob(); + } + /** * @return true if the job is exempted from Doze restrictions and therefore allowed to run * in Doze. */ public boolean canRunInDoze() { - return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; + return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 || shouldTreatAsForegroundJob(); } boolean canRunInBatterySaver() { - return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0; + return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0 + || shouldTreatAsForegroundJob(); } boolean shouldIgnoreNetworkBlocking() { - return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; + return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 || shouldTreatAsForegroundJob(); } /** @return true if the constraint was changed, false otherwise. */ @@ -1128,6 +1146,16 @@ public final class JobStatus { return false; } + /** @return true if the constraint was changed, false otherwise. */ + boolean setForegroundJobQuotaConstraintSatisfied(boolean state) { + if (setConstraintSatisfied(CONSTRAINT_WITHIN_HPJ_QUOTA, state)) { + // The constraint was changed. Update the ready flag. + mReadyWithinHpjQuota = state; + return true; + } + return false; + } + /** @return true if the state was changed, false otherwise. */ boolean setUidActive(final boolean newActiveState) { if (newActiveState != uidActive) { @@ -1257,6 +1285,10 @@ public final class JobStatus { oldValue = mReadyWithinQuota; mReadyWithinQuota = true; break; + case CONSTRAINT_WITHIN_HPJ_QUOTA: + oldValue = mReadyWithinHpjQuota; + mReadyWithinHpjQuota = true; + break; default: satisfied |= constraint; mReadyDynamicSatisfied = mDynamicConstraints != 0 @@ -1279,6 +1311,9 @@ public final class JobStatus { case CONSTRAINT_WITHIN_QUOTA: mReadyWithinQuota = oldValue; break; + case CONSTRAINT_WITHIN_HPJ_QUOTA: + mReadyWithinHpjQuota = oldValue; + break; default: mReadyDynamicSatisfied = mDynamicConstraints != 0 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); @@ -1293,7 +1328,7 @@ public final class JobStatus { // sessions (exempt from dynamic restrictions), we need the additional check to ensure // that NEVER jobs don't run. // TODO: cleanup quota and standby bucket management so we don't need the additional checks - if ((!mReadyWithinQuota && !mReadyDynamicSatisfied) + if ((!mReadyWithinQuota && !mReadyDynamicSatisfied && !shouldTreatAsForegroundJob()) || getEffectiveStandbyBucket() == NEVER_INDEX) { return false; } @@ -1506,6 +1541,9 @@ public final class JobStatus { if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { pw.print(" WITHIN_QUOTA"); } + if ((constraints & CONSTRAINT_WITHIN_HPJ_QUOTA) != 0) { + pw.print(" WITHIN_HPJ_QUOTA"); + } if (constraints != 0) { pw.print(" [0x"); pw.print(Integer.toHexString(constraints)); @@ -1578,6 +1616,9 @@ public final class JobStatus { if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) { proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED); } + if ((constraints & CONSTRAINT_WITHIN_HPJ_QUOTA) != 0) { + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_HPJ_QUOTA); + } } private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) { @@ -1795,6 +1836,11 @@ public final class JobStatus { pw.print(prefix); pw.print(" readyComponentEnabled: "); pw.println(serviceInfo != null); + if ((getFlags() & JobInfo.FLAG_FOREGROUND_JOB) != 0) { + pw.print(prefix); + pw.print(" mReadyWithinHpjQuota: "); + pw.println(mReadyWithinHpjQuota); + } if (changedAuthorities != null) { pw.print(prefix); pw.println("Changed authorities:"); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 1d72b42d824d..a3a20c6c1091 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -36,6 +36,9 @@ import android.app.ActivityManagerInternal; import android.app.AlarmManager; import android.app.AppGlobals; import android.app.IUidObserver; +import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManagerInternal; +import android.app.usage.UsageStatsManagerInternal.UsageEventListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -53,6 +56,7 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Pair; import android.util.Slog; +import android.util.SparseArray; import android.util.SparseArrayMap; import android.util.SparseBooleanArray; import android.util.SparseSetArray; @@ -297,10 +301,17 @@ public final class QuotaController extends StateController { /** Timer for each package-userId combo. */ private final SparseArrayMap<String, Timer> mPkgTimers = new SparseArrayMap<>(); - /** List of all timing sessions for a package-userId combo, in chronological order. */ + /** Timer for HPJs for each package-userId combo. */ + private final SparseArrayMap<String, Timer> mHpjPkgTimers = new SparseArrayMap<>(); + + /** List of all regular timing sessions for a package-userId combo, in chronological order. */ private final SparseArrayMap<String, List<TimingSession>> mTimingSessions = new SparseArrayMap<>(); + /** List of all hpj timing sessions for a package-userId combo, in chronological order. */ + private final SparseArrayMap<String, List<TimingSession>> mHpjTimingSessions = + new SparseArrayMap<>(); + /** * Listener to track and manage when each package comes back within quota. */ @@ -311,6 +322,10 @@ public final class QuotaController extends StateController { private final SparseArrayMap<String, ExecutionStats[]> mExecutionStatsCache = new SparseArrayMap<>(); + private final SparseArrayMap<String, ShrinkableDebits> mHpjStats = new SparseArrayMap<>(); + + private final SparseArrayMap<String, TopAppTimer> mTopAppTrackers = new SparseArrayMap<>(); + /** List of UIDs currently in the foreground. */ private final SparseBooleanArray mForegroundUids = new SparseBooleanArray(); @@ -327,7 +342,7 @@ public final class QuotaController extends StateController { private final ActivityManagerInternal mActivityManagerInternal; private final AlarmManager mAlarmManager; private final ChargingTracker mChargeTracker; - private final Handler mHandler; + private final QcHandler mHandler; private final QcConstants mQcConstants; /** How much time each app will have to run jobs within their standby bucket window. */ @@ -474,14 +489,63 @@ public final class QuotaController extends StateController { private long mTimingSessionCoalescingDurationMs = QcConstants.DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS; + /** + * The rolling window size for each standby bucket. Within each window, an app will have 10 + * minutes to run its jobs. + */ + private final long[] mHpjLimitsMs = new long[]{ + QcConstants.DEFAULT_HPJ_LIMIT_ACTIVE_MS, + QcConstants.DEFAULT_HPJ_LIMIT_WORKING_MS, + QcConstants.DEFAULT_HPJ_LIMIT_FREQUENT_MS, + QcConstants.DEFAULT_HPJ_LIMIT_RARE_MS, + 0, // NEVER + QcConstants.DEFAULT_HPJ_LIMIT_RESTRICTED_MS + }; + + /** + * The period of time used to calculate HPJ sessions. Apps can only have HPJ sessions + * totalling {@link #mHpjLimitsMs}[bucket within this period of time (without factoring in any + * rewards or free HPJs). + */ + private long mHpjLimitWindowSizeMs = QcConstants.DEFAULT_HPJ_WINDOW_SIZE_MS; + + /** + * Length of time used to split an app's top time into chunks. + */ + public long mHpjTopAppTimeChunkSizeMs = QcConstants.DEFAULT_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS; + + /** + * How much HPJ quota to give back to an app based on the number of top app time chunks it had. + */ + public long mHpjRewardTopAppMs = QcConstants.DEFAULT_HPJ_REWARD_TOP_APP_MS; + + /** + * How much HPJ quota to give back to an app based on each non-top user interaction. + */ + public long mHpjRewardInteractionMs = QcConstants.DEFAULT_HPJ_REWARD_INTERACTION_MS; + + /** + * How much HPJ quota to give back to an app based on each notification seen event. + */ + public long mHpjRewardNotificationSeenMs = QcConstants.DEFAULT_HPJ_REWARD_NOTIFICATION_SEEN_MS; + /** An app has reached its quota. The message should contain a {@link Package} object. */ - private static final int MSG_REACHED_QUOTA = 0; + @VisibleForTesting + static final int MSG_REACHED_QUOTA = 0; /** Drop any old timing sessions. */ private static final int MSG_CLEAN_UP_SESSIONS = 1; /** Check if a package is now within its quota. */ private static final int MSG_CHECK_PACKAGE = 2; /** Process state for a UID has changed. */ private static final int MSG_UID_PROCESS_STATE_CHANGED = 3; + /** An app has reached its HPJ quota. The message should contain a {@link Package} object. */ + @VisibleForTesting + static final int MSG_REACHED_HPJ_QUOTA = 4; + /** + * Process a new {@link UsageEvents.Event}. The event will be the message's object and the + * userId will the first arg. + */ + private static final int MSG_PROCESS_USAGE_EVENT = 5; public QuotaController(JobSchedulerService service) { super(service); @@ -499,6 +563,9 @@ public final class QuotaController extends StateController { AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class); appStandby.addListener(new StandbyTracker()); + UsageStatsManagerInternal usmi = LocalServices.getService(UsageStatsManagerInternal.class); + usmi.registerListener(new UsageEventTracker()); + try { ActivityManager.getService().registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE, @@ -521,7 +588,15 @@ public final class QuotaController extends StateController { jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA); final boolean isWithinQuota = isWithinQuotaLocked(jobStatus); setConstraintSatisfied(jobStatus, isWithinQuota); - if (!isWithinQuota) { + final boolean outOfHpjQuota; + if (jobStatus.isRequestedForegroundJob()) { + final boolean isWithinHpjQuota = isWithinHpjQuotaLocked(jobStatus); + jobStatus.setForegroundJobQuotaConstraintSatisfied(isWithinHpjQuota); + outOfHpjQuota = !isWithinHpjQuota; + } else { + outOfHpjQuota = false; + } + if (!isWithinQuota || outOfHpjQuota) { maybeScheduleStartAlarmLocked(userId, pkgName, jobStatus.getEffectiveStandbyBucket()); } } @@ -544,10 +619,12 @@ public final class QuotaController extends StateController { final int userId = jobStatus.getSourceUserId(); final String packageName = jobStatus.getSourcePackageName(); - Timer timer = mPkgTimers.get(userId, packageName); + final SparseArrayMap<String, Timer> timerMap = + jobStatus.shouldTreatAsForegroundJob() ? mHpjPkgTimers : mPkgTimers; + Timer timer = timerMap.get(userId, packageName); if (timer == null) { - timer = new Timer(uid, userId, packageName); - mPkgTimers.add(userId, packageName, timer); + timer = new Timer(uid, userId, packageName, !jobStatus.shouldTreatAsForegroundJob()); + timerMap.add(userId, packageName, timer); } timer.startTrackingJobLocked(jobStatus); } @@ -561,6 +638,13 @@ public final class QuotaController extends StateController { if (timer != null) { timer.stopTrackingJob(jobStatus); } + if (jobStatus.isRequestedForegroundJob()) { + timer = mHpjPkgTimers.get(jobStatus.getSourceUserId(), + jobStatus.getSourcePackageName()); + if (timer != null) { + timer.stopTrackingJob(jobStatus); + } + } ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); if (jobs != null) { @@ -585,26 +669,37 @@ public final class QuotaController extends StateController { public void onUserRemovedLocked(int userId) { mTrackedJobs.delete(userId); mPkgTimers.delete(userId); + mHpjPkgTimers.delete(userId); mTimingSessions.delete(userId); + mHpjTimingSessions.delete(userId); mInQuotaAlarmListener.removeAlarmsLocked(userId); mExecutionStatsCache.delete(userId); + mHpjStats.delete(userId); mUidToPackageCache.clear(); } /** Drop all historical stats and stop tracking any active sessions for the specified app. */ public void clearAppStatsLocked(int userId, @NonNull String packageName) { mTrackedJobs.delete(userId, packageName); - Timer timer = mPkgTimers.get(userId, packageName); + Timer timer = mPkgTimers.delete(userId, packageName); if (timer != null) { if (timer.isActive()) { Slog.e(TAG, "clearAppStats called before Timer turned off."); timer.dropEverythingLocked(); } - mPkgTimers.delete(userId, packageName); + } + timer = mHpjPkgTimers.delete(userId, packageName); + if (timer != null) { + if (timer.isActive()) { + Slog.e(TAG, "clearAppStats called before HPJ Timer turned off."); + timer.dropEverythingLocked(); + } } mTimingSessions.delete(userId, packageName); + mHpjTimingSessions.delete(userId, packageName); mInQuotaAlarmListener.removeAlarmLocked(userId, packageName); mExecutionStatsCache.delete(userId, packageName); + mHpjStats.delete(userId, packageName); } private boolean isUidInForeground(int uid) { @@ -627,11 +722,53 @@ public final class QuotaController extends StateController { if (mChargeTracker.isCharging() || isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) { - return JobServiceContext.EXECUTING_TIMESLICE_MILLIS; + return JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS; + } + if (jobStatus.shouldTreatAsForegroundJob()) { + return jobStatus.getStandbyBucket() == RESTRICTED_INDEX + ? JobServiceContext.DEFAULT_RESTRICTED_HPJ_EXECUTING_TIMESLICE_MILLIS + : JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS; } return getRemainingExecutionTimeLocked(jobStatus); } + /** @return true if the job is within hpj quota. */ + public boolean isWithinHpjQuotaLocked(@NonNull final JobStatus jobStatus) { + if (isQuotaFree(jobStatus.getEffectiveStandbyBucket())) { + return true; + } + // A job is within quota if one of the following is true: + // 1. it's already running (already executing HPJS should be allowed to finish) + // 2. the app is currently in the foreground + // 3. the app overall is within its quota + if (isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) { + return true; + } + Timer hpjTimer = mHpjPkgTimers.get(jobStatus.getSourceUserId(), + jobStatus.getSourcePackageName()); + // Any already executing HPJs should be allowed to finish. + if (hpjTimer != null && hpjTimer.isRunning(jobStatus)) { + return true; + } + + return 0 < getRemainingHpjExecutionTimeLocked( + jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); + } + + @NonNull + private ShrinkableDebits getHpjQuotaLocked(final int userId, + @NonNull final String packageName) { + ShrinkableDebits debits = mHpjStats.get(userId, packageName); + if (debits == null) { + debits = new ShrinkableDebits( + JobSchedulerService.standbyBucketForPackage( + packageName, userId, sElapsedRealtimeClock.millis()) + ); + mHpjStats.add(userId, packageName, debits); + } + return debits; + } + @VisibleForTesting boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) { final int standbyBucket = jobStatus.getEffectiveStandbyBucket(); @@ -645,19 +782,22 @@ public final class QuotaController extends StateController { jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket); } - @VisibleForTesting - boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName, - final int standbyBucket) { - if (standbyBucket == NEVER_INDEX) return false; - + private boolean isQuotaFree(final int standbyBucket) { // Quota constraint is not enforced while charging. if (mChargeTracker.isCharging()) { // Restricted jobs require additional constraints when charging, so don't immediately // mark quota as free when charging. - if (standbyBucket != RESTRICTED_INDEX) { - return true; - } + return standbyBucket != RESTRICTED_INDEX; } + return false; + } + + @VisibleForTesting + boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName, + final int standbyBucket) { + if (standbyBucket == NEVER_INDEX) return false; + + if (isQuotaFree(standbyBucket)) return true; ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket); return getRemainingExecutionTimeLocked(stats) > 0 @@ -717,6 +857,49 @@ public final class QuotaController extends StateController { mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs); } + @VisibleForTesting + long getRemainingHpjExecutionTimeLocked(final int userId, @NonNull final String packageName) { + ShrinkableDebits quota = getHpjQuotaLocked(userId, packageName); + if (quota.getStandbyBucketLocked() == NEVER_INDEX) { + return 0; + } + final long limitMs = mHpjLimitsMs[quota.getStandbyBucketLocked()]; + long remainingMs = limitMs - quota.getTallyLocked(); + + // Stale sessions may still be factored into tally. Make sure they're removed. + List<TimingSession> timingSessions = mHpjTimingSessions.get(userId, packageName); + final long nowElapsed = sElapsedRealtimeClock.millis(); + final long windowStartTimeElapsed = nowElapsed - mHpjLimitWindowSizeMs; + if (timingSessions != null) { + while (timingSessions.size() > 0) { + TimingSession ts = timingSessions.get(0); + if (ts.endTimeElapsed < windowStartTimeElapsed) { + final long duration = ts.endTimeElapsed - ts.startTimeElapsed; + remainingMs += duration; + quota.transactOnDebitsLocked(-duration); + timingSessions.remove(0); + } else if (ts.startTimeElapsed < windowStartTimeElapsed) { + remainingMs += windowStartTimeElapsed - ts.startTimeElapsed; + break; + } else { + // Fully within the window. + break; + } + } + } + + Timer timer = mHpjPkgTimers.get(userId, packageName); + if (timer == null) { + return remainingMs; + } + // There's a case where the debits tally is 0 but a currently running HPJ still counts + // towards quota. If the app gets a reward in this case, the reward is lost and the HPJ + // run is still fully counted. + // TODO(171305774)/STOPSHIP: make sure getting rewards while HPJ currently executing isn't + // treated negatively + return remainingMs - timer.getCurrentDuration(sElapsedRealtimeClock.millis()); + } + /** * Returns the amount of time, in milliseconds, until the package would have reached its * duration quota, assuming it has a job counting towards its quota the entire time. This takes @@ -802,6 +985,61 @@ public final class QuotaController extends StateController { return timeUntilQuotaConsumedMs; } + /** + * Returns the amount of time, in milliseconds, until the package would have reached its HPJ + * quota, assuming it has a job counting towards the quota the entire time and the quota isn't + * replenished at all in that time. + */ + @VisibleForTesting + long getTimeUntilHpjQuotaConsumedLocked(final int userId, @NonNull final String packageName) { + final long remainingExecutionTimeMs = + getRemainingHpjExecutionTimeLocked(userId, packageName); + + List<TimingSession> sessions = mHpjTimingSessions.get(userId, packageName); + if (sessions == null || sessions.size() == 0) { + return remainingExecutionTimeMs; + } + + final long nowElapsed = sElapsedRealtimeClock.millis(); + ShrinkableDebits quota = getHpjQuotaLocked(userId, packageName); + final long limitMs = mHpjLimitsMs[quota.getStandbyBucketLocked()]; + final long startWindowElapsed = Math.max(0, nowElapsed - mHpjLimitWindowSizeMs); + long remainingDeadSpaceMs = remainingExecutionTimeMs; + // Total time looked at where a session wouldn't be phasing out. + long deadSpaceMs = 0; + // Time regained from sessions phasing out + long phasedOutSessionTimeMs = 0; + + for (int i = 0; i < sessions.size(); ++i) { + TimingSession session = sessions.get(i); + if (session.endTimeElapsed < startWindowElapsed) { + // Edge case where a session became stale in the time between the call to + // getRemainingHpjExecutionTimeLocked and this line. + remainingDeadSpaceMs += session.endTimeElapsed - session.startTimeElapsed; + sessions.remove(i); + i--; + } else if (session.startTimeElapsed < startWindowElapsed) { + // Session straddles start of window + phasedOutSessionTimeMs = session.endTimeElapsed - startWindowElapsed; + } else { + // Session fully inside window + final long timeBetweenSessions = session.startTimeElapsed + - (i == 0 ? startWindowElapsed : sessions.get(i - 1).endTimeElapsed); + final long usedDeadSpaceMs = Math.min(remainingDeadSpaceMs, timeBetweenSessions); + deadSpaceMs += usedDeadSpaceMs; + if (usedDeadSpaceMs == timeBetweenSessions) { + phasedOutSessionTimeMs += session.endTimeElapsed - session.startTimeElapsed; + } + remainingDeadSpaceMs -= usedDeadSpaceMs; + if (remainingDeadSpaceMs <= 0) { + break; + } + } + } + + return Math.min(limitMs, deadSpaceMs + phasedOutSessionTimeMs + remainingDeadSpaceMs); + } + /** Returns the execution stats of the app in the most recent window. */ @VisibleForTesting @NonNull @@ -1053,18 +1291,36 @@ public final class QuotaController extends StateController { @VisibleForTesting void saveTimingSession(final int userId, @NonNull final String packageName, - @NonNull final TimingSession session) { + @NonNull final TimingSession session, boolean isHpj) { synchronized (mLock) { - List<TimingSession> sessions = mTimingSessions.get(userId, packageName); + final SparseArrayMap<String, List<TimingSession>> sessionMap = + isHpj ? mHpjTimingSessions : mTimingSessions; + List<TimingSession> sessions = sessionMap.get(userId, packageName); if (sessions == null) { sessions = new ArrayList<>(); - mTimingSessions.add(userId, packageName, sessions); + sessionMap.add(userId, packageName, sessions); } sessions.add(session); - // Adding a new session means that the current stats are now incorrect. - invalidateAllExecutionStatsLocked(userId, packageName); + if (isHpj) { + final ShrinkableDebits quota = getHpjQuotaLocked(userId, packageName); + quota.transactOnDebitsLocked(session.endTimeElapsed - session.startTimeElapsed); + } else { + // Adding a new session means that the current stats are now incorrect. + invalidateAllExecutionStatsLocked(userId, packageName); - maybeScheduleCleanupAlarmLocked(); + maybeScheduleCleanupAlarmLocked(); + } + } + } + + private void grantRewardForInstantEvent( + final int userId, @NonNull final String packageName, final long credit) { + synchronized (mLock) { + final ShrinkableDebits quota = getHpjQuotaLocked(userId, packageName); + quota.transactOnDebitsLocked(-credit); + if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { + mStateChangedListener.onControllerStateChanged(); + } } } @@ -1100,6 +1356,7 @@ public final class QuotaController extends StateController { } mEarliestEndTimeFunctor.reset(); mTimingSessions.forEach(mEarliestEndTimeFunctor); + mHpjTimingSessions.forEach(mEarliestEndTimeFunctor); final long earliestEndElapsed = mEarliestEndTimeFunctor.earliestEndElapsed; if (earliestEndElapsed == Long.MAX_VALUE) { // Couldn't find a good time to clean up. Maybe this was called after we deleted all @@ -1155,6 +1412,7 @@ public final class QuotaController extends StateController { Slog.d(TAG, "handleNewChargingStateLocked: " + mChargeTracker.isCharging()); } // Deal with Timers first. + mHpjPkgTimers.forEach(mTimerChargingUpdateFunctor); mPkgTimers.forEach(mTimerChargingUpdateFunctor); // Now update jobs. maybeUpdateAllConstraintsLocked(); @@ -1189,6 +1447,7 @@ public final class QuotaController extends StateController { // Quota is the same for all jobs within a package. final int realStandbyBucket = jobs.valueAt(0).getStandbyBucket(); final boolean realInQuota = isWithinQuotaLocked(userId, packageName, realStandbyBucket); + boolean outOfHpjQuota = false; boolean changed = false; for (int i = jobs.size() - 1; i >= 0; --i) { final JobStatus js = jobs.valueAt(i); @@ -1206,8 +1465,14 @@ public final class QuotaController extends StateController { // This job is somehow exempted. Need to determine its own quota status. changed |= setConstraintSatisfied(js, isWithinQuotaLocked(js)); } + + if (js.isRequestedForegroundJob()) { + boolean isWithinHpjQuota = isWithinHpjQuotaLocked(js); + changed |= js.setForegroundJobQuotaConstraintSatisfied(isWithinHpjQuota); + outOfHpjQuota |= !isWithinHpjQuota; + } } - if (!realInQuota) { + if (!realInQuota || outOfHpjQuota) { // Don't want to use the effective standby bucket here since that bump the bucket to // ACTIVE for one of the jobs, which doesn't help with other jobs that aren't // exempted. @@ -1226,10 +1491,22 @@ public final class QuotaController extends StateController { @Override public void accept(JobStatus jobStatus) { wasJobChanged |= setConstraintSatisfied(jobStatus, isWithinQuotaLocked(jobStatus)); + final boolean outOfHpjQuota; + if (jobStatus.isRequestedForegroundJob()) { + final boolean isWithinHpjQuota = isWithinHpjQuotaLocked(jobStatus); + wasJobChanged |= jobStatus.setForegroundJobQuotaConstraintSatisfied( + isWithinHpjQuota); + outOfHpjQuota = !isWithinHpjQuota; + } else { + outOfHpjQuota = false; + } + final int userId = jobStatus.getSourceUserId(); final String packageName = jobStatus.getSourcePackageName(); final int realStandbyBucket = jobStatus.getStandbyBucket(); - if (isWithinQuotaLocked(userId, packageName, realStandbyBucket)) { + if (isWithinQuotaLocked(userId, packageName, realStandbyBucket) && !outOfHpjQuota) { + // TODO(141645789): we probably shouldn't cancel the alarm until we've verified + // that all jobs for the userId-package are within quota. mInQuotaAlarmListener.removeAlarmLocked(userId, packageName); } else { mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket); @@ -1280,11 +1557,13 @@ public final class QuotaController extends StateController { final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket); final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats, standbyBucket); + final long remainingHpjQuota = getRemainingHpjExecutionTimeLocked(userId, packageName); if (stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs && isUnderJobCountQuota - && isUnderTimingSessionCountQuota) { + && isUnderTimingSessionCountQuota + && remainingHpjQuota > 0) { // Already in quota. Why was this method called? if (DEBUG) { Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString @@ -1297,19 +1576,44 @@ public final class QuotaController extends StateController { return; } - // The time this app will have quota again. - long inQuotaTimeElapsed = stats.inQuotaTimeElapsed; - if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) { - // App hit the rate limit. - inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed, - stats.jobRateLimitExpirationTimeElapsed); - } - if (!isUnderTimingSessionCountQuota - && stats.sessionCountInWindow < stats.sessionCountLimit) { - // App hit the rate limit. - inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed, - stats.sessionRateLimitExpirationTimeElapsed); + long inRegularQuotaTimeElapsed = Long.MAX_VALUE; + long inHpjQuotaTimeElapsed = Long.MAX_VALUE; + if (!(stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs + && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs + && isUnderJobCountQuota + && isUnderTimingSessionCountQuota)) { + // The time this app will have quota again. + long inQuotaTimeElapsed = stats.inQuotaTimeElapsed; + if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) { + // App hit the rate limit. + inQuotaTimeElapsed = + Math.max(inQuotaTimeElapsed, stats.jobRateLimitExpirationTimeElapsed); + } + if (!isUnderTimingSessionCountQuota + && stats.sessionCountInWindow < stats.sessionCountLimit) { + // App hit the rate limit. + inQuotaTimeElapsed = + Math.max(inQuotaTimeElapsed, stats.sessionRateLimitExpirationTimeElapsed); + } + inRegularQuotaTimeElapsed = inQuotaTimeElapsed; + } + if (remainingHpjQuota <= 0) { + final long limitMs = mHpjLimitsMs[standbyBucket] - mQuotaBufferMs; + List<TimingSession> timingSessions = mHpjTimingSessions.get(userId, packageName); + long sumMs = 0; + for (int i = timingSessions.size() - 1; i >= 0; --i) { + TimingSession ts = timingSessions.get(i); + final long durationMs = ts.endTimeElapsed - ts.startTimeElapsed; + sumMs += durationMs; + if (sumMs >= limitMs) { + inHpjQuotaTimeElapsed = + ts.startTimeElapsed + (sumMs - limitMs) + mHpjLimitWindowSizeMs; + break; + } + } } + long inQuotaTimeElapsed = Math.min(inRegularQuotaTimeElapsed, inHpjQuotaTimeElapsed); + if (inQuotaTimeElapsed <= sElapsedRealtimeClock.millis()) { final long nowElapsed = sElapsedRealtimeClock.millis(); Slog.wtf(TAG, @@ -1450,9 +1754,52 @@ public final class QuotaController extends StateController { } } + private static final class ShrinkableDebits { + /** The amount of quota remaining. Can be negative if limit changes. */ + private long mDebitTally; + private int mStandbyBucket; + + ShrinkableDebits(int standbyBucket) { + mDebitTally = 0; + mStandbyBucket = standbyBucket; + } + + long getTallyLocked() { + return mDebitTally; + } + + /** + * Negative if the tally should decrease (therefore increasing available quota); + * or positive if the tally should increase (therefore decreasing available quota). + */ + void transactOnDebitsLocked(final long amount) { + mDebitTally = Math.max(0, mDebitTally + amount); + } + + void setStandbyBucketLocked(int standbyBucket) { + mStandbyBucket = standbyBucket; + } + + int getStandbyBucketLocked() { + return mStandbyBucket; + } + + @Override + public String toString() { + return "ShrinkableDebits { debit tally: " + + mDebitTally + ", bucket: " + mStandbyBucket + + " }"; + } + + void dumpLocked(IndentingPrintWriter pw) { + pw.println(toString()); + } + } + private final class Timer { private final Package mPkg; private final int mUid; + private final boolean mRegularJobTimer; // List of jobs currently running for this app that started when the app wasn't in the // foreground. @@ -1460,9 +1807,10 @@ public final class QuotaController extends StateController { private long mStartTimeElapsed; private int mBgJobCount; - Timer(int uid, int userId, String packageName) { + Timer(int uid, int userId, String packageName, boolean regularJobTimer) { mPkg = new Package(userId, packageName); mUid = uid; + mRegularJobTimer = regularJobTimer; } void startTrackingJobLocked(@NonNull JobStatus jobStatus) { @@ -1482,12 +1830,17 @@ public final class QuotaController extends StateController { mRunningBgJobs.add(jobStatus); if (shouldTrackLocked()) { mBgJobCount++; - incrementJobCountLocked(mPkg.userId, mPkg.packageName, 1); + if (mRegularJobTimer) { + incrementJobCountLocked(mPkg.userId, mPkg.packageName, 1); + } if (mRunningBgJobs.size() == 1) { // Started tracking the first job. mStartTimeElapsed = sElapsedRealtimeClock.millis(); - // Starting the timer means that all cached execution stats are now incorrect. - invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName); + if (mRegularJobTimer) { + // Starting the timer means that all cached execution stats are now + // incorrect. + invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName); + } scheduleCutoff(); } } @@ -1529,13 +1882,15 @@ public final class QuotaController extends StateController { return; } TimingSession ts = new TimingSession(mStartTimeElapsed, nowElapsed, mBgJobCount); - saveTimingSession(mPkg.userId, mPkg.packageName, ts); + saveTimingSession(mPkg.userId, mPkg.packageName, ts, !mRegularJobTimer); mBgJobCount = 0; // Don't reset the tracked jobs list as we need to keep tracking the current number // of jobs. // However, cancel the currently scheduled cutoff since it's not currently useful. cancelCutoff(); - incrementTimingSessionCountLocked(mPkg.userId, mPkg.packageName); + if (mRegularJobTimer) { + incrementTimingSessionCountLocked(mPkg.userId, mPkg.packageName); + } } /** @@ -1582,10 +1937,13 @@ public final class QuotaController extends StateController { // repeatedly plugged in and unplugged, or an app changes foreground state // very frequently, the job count for a package may be artificially high. mBgJobCount = mRunningBgJobs.size(); - incrementJobCountLocked(mPkg.userId, mPkg.packageName, mBgJobCount); - // Starting the timer means that all cached execution stats are now - // incorrect. - invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName); + + if (mRegularJobTimer) { + incrementJobCountLocked(mPkg.userId, mPkg.packageName, mBgJobCount); + // Starting the timer means that all cached execution stats are now + // incorrect. + invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName); + } // Schedule cutoff since we're now actively tracking for quotas again. scheduleCutoff(); } @@ -1605,11 +1963,15 @@ public final class QuotaController extends StateController { if (!isActive()) { return; } - Message msg = mHandler.obtainMessage(MSG_REACHED_QUOTA, mPkg); - final long timeRemainingMs = getTimeUntilQuotaConsumedLocked(mPkg.userId, - mPkg.packageName); + Message msg = mHandler.obtainMessage( + mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_HPJ_QUOTA, mPkg); + final long timeRemainingMs = mRegularJobTimer + ? getTimeUntilQuotaConsumedLocked(mPkg.userId, mPkg.packageName) + : getTimeUntilHpjQuotaConsumedLocked(mPkg.userId, mPkg.packageName); if (DEBUG) { - Slog.i(TAG, "Job for " + mPkg + " has " + timeRemainingMs + "ms left."); + Slog.i(TAG, + (mRegularJobTimer ? "Regular job" : "HPJ") + " for " + mPkg + " has " + + timeRemainingMs + "ms left."); } // If the job was running the entire time, then the system would be up, so it's // fine to use uptime millis for these messages. @@ -1618,11 +1980,14 @@ public final class QuotaController extends StateController { } private void cancelCutoff() { - mHandler.removeMessages(MSG_REACHED_QUOTA, mPkg); + mHandler.removeMessages( + mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_HPJ_QUOTA, mPkg); } public void dump(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { - pw.print("Timer{"); + pw.print("Timer<"); + pw.print(mRegularJobTimer ? "REG" : " HPJ"); + pw.print(">{"); pw.print(mPkg); pw.print("} "); if (isActive()) { @@ -1668,6 +2033,98 @@ public final class QuotaController extends StateController { } } + private final class TopAppTimer { + private final Package mPkg; + + // List of jobs currently running for this app that started when the app wasn't in the + // foreground. + private final SparseArray<UsageEvents.Event> mActivities = new SparseArray<>(); + private long mStartTimeElapsed; + + TopAppTimer(int userId, String packageName) { + mPkg = new Package(userId, packageName); + } + + void processEventLocked(@NonNull UsageEvents.Event event) { + final long nowElapsed = sElapsedRealtimeClock.millis(); + switch (event.getEventType()) { + case UsageEvents.Event.ACTIVITY_RESUMED: + if (mActivities.size() == 0) { + mStartTimeElapsed = nowElapsed; + } + mActivities.put(event.mInstanceId, event); + break; + case UsageEvents.Event.ACTIVITY_PAUSED: + case UsageEvents.Event.ACTIVITY_STOPPED: + case UsageEvents.Event.ACTIVITY_DESTROYED: + final UsageEvents.Event existingEvent = + mActivities.removeReturnOld(event.mInstanceId); + if (existingEvent != null && mActivities.size() == 0) { + final long totalTopTimeMs = nowElapsed - mStartTimeElapsed; + int numTimeChunks = (int) (totalTopTimeMs / mHpjTopAppTimeChunkSizeMs); + final long remainderMs = totalTopTimeMs % mHpjTopAppTimeChunkSizeMs; + if (remainderMs >= SECOND_IN_MILLIS) { + // "Round up" + numTimeChunks++; + } + if (DEBUG) { + Slog.d(TAG, + "Crediting " + mPkg + " for " + numTimeChunks + " time chunks"); + } + final ShrinkableDebits quota = + getHpjQuotaLocked(mPkg.userId, mPkg.packageName); + quota.transactOnDebitsLocked(-mHpjRewardTopAppMs * numTimeChunks); + if (maybeUpdateConstraintForPkgLocked(mPkg.userId, mPkg.packageName)) { + mStateChangedListener.onControllerStateChanged(); + } + } + break; + } + } + + boolean isActive() { + synchronized (mLock) { + return mActivities.size() > 0; + } + } + + public void dump(IndentingPrintWriter pw) { + pw.print("TopAppTimer{"); + pw.print(mPkg); + pw.print("} "); + if (isActive()) { + pw.print("started at "); + pw.print(mStartTimeElapsed); + pw.print(" ("); + pw.print(sElapsedRealtimeClock.millis() - mStartTimeElapsed); + pw.print("ms ago)"); + } else { + pw.print("NOT active"); + } + pw.println(); + pw.increaseIndent(); + for (int i = 0; i < mActivities.size(); i++) { + UsageEvents.Event event = mActivities.valueAt(i); + pw.println(event.getClassName()); + } + pw.decreaseIndent(); + } + + public void dump(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + + mPkg.dumpDebug(proto, StateControllerProto.QuotaController.TopAppTimer.PKG); + proto.write(StateControllerProto.QuotaController.TopAppTimer.IS_ACTIVE, isActive()); + proto.write(StateControllerProto.QuotaController.TopAppTimer.START_TIME_ELAPSED, + mStartTimeElapsed); + proto.write(StateControllerProto.QuotaController.TopAppTimer.ACTIVITY_COUNT, + mActivities.size()); + // TODO: maybe dump activities/events + + proto.end(token); + } + } + /** * Tracking of app assignments to standby buckets */ @@ -1693,8 +2150,14 @@ public final class QuotaController extends StateController { } List<JobStatus> restrictedChanges = new ArrayList<>(); synchronized (mLock) { + ShrinkableDebits debits = mHpjStats.get(userId, packageName); + if (debits != null) { + debits.setStandbyBucketLocked(bucketIndex); + } + ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName); if (jobs == null || jobs.size() == 0) { + // Nothing further to do. return; } for (int i = jobs.size() - 1; i >= 0; i--) { @@ -1711,6 +2174,10 @@ public final class QuotaController extends StateController { if (timer != null && timer.isActive()) { timer.rescheduleCutoff(); } + timer = mHpjPkgTimers.get(userId, packageName); + if (timer != null && timer.isActive()) { + timer.rescheduleCutoff(); + } if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { mStateChangedListener.onControllerStateChanged(); } @@ -1720,6 +2187,16 @@ public final class QuotaController extends StateController { } } + final class UsageEventTracker implements UsageEventListener { + /** + * Callback to inform listeners of a new event. + */ + @Override + public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) { + mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event); + } + } + private final class DeleteTimingSessionsFunctor implements Consumer<List<TimingSession>> { private final Predicate<TimingSession> mTooOld = new Predicate<TimingSession>() { public boolean test(TimingSession ts) { @@ -1742,9 +2219,13 @@ public final class QuotaController extends StateController { @VisibleForTesting void deleteObsoleteSessionsLocked() { mTimingSessions.forEach(mDeleteOldSessionsFunctor); + // Don't delete HPJ timing sessions here. They'll be removed in + // getRemainingHpjExecutionTimeLocked(). } private class QcHandler extends Handler { + private boolean mIsProcessing; + QcHandler(Looper looper) { super(looper); } @@ -1752,6 +2233,8 @@ public final class QuotaController extends StateController { @Override public void handleMessage(Message msg) { synchronized (mLock) { + mIsProcessing = true; + switch (msg.what) { case MSG_REACHED_QUOTA: { Package pkg = (Package) msg.obj; @@ -1781,6 +2264,33 @@ public final class QuotaController extends StateController { } break; } + case MSG_REACHED_HPJ_QUOTA: { + Package pkg = (Package) msg.obj; + if (DEBUG) { + Slog.d(TAG, "Checking if " + pkg + " has reached its HPJ quota."); + } + + long timeRemainingMs = getRemainingHpjExecutionTimeLocked( + pkg.userId, pkg.packageName); + if (timeRemainingMs <= 0) { + if (DEBUG) Slog.d(TAG, pkg + " has reached its HPJ quota."); + if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) { + mStateChangedListener.onControllerStateChanged(); + } + } else { + // This could potentially happen if an old session phases out while a + // job is currently running. + // Reschedule message + Message rescheduleMsg = obtainMessage(MSG_REACHED_HPJ_QUOTA, pkg); + timeRemainingMs = getTimeUntilHpjQuotaConsumedLocked( + pkg.userId, pkg.packageName); + if (DEBUG) { + Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left for HPJ"); + } + sendMessageDelayed(rescheduleMsg, timeRemainingMs); + } + break; + } case MSG_CLEAN_UP_SESSIONS: if (DEBUG) { Slog.d(TAG, "Cleaning up timing sessions."); @@ -1816,7 +2326,8 @@ public final class QuotaController extends StateController { isQuotaFree = false; } // Update Timers first. - if (mPkgTimers.indexOfKey(userId) >= 0) { + if (mPkgTimers.indexOfKey(userId) >= 0 + || mHpjPkgTimers.indexOfKey(userId) >= 0) { ArraySet<String> packages = mUidToPackageCache.get(uid); if (packages == null) { try { @@ -1834,7 +2345,11 @@ public final class QuotaController extends StateController { } if (packages != null) { for (int i = packages.size() - 1; i >= 0; --i) { - Timer t = mPkgTimers.get(userId, packages.valueAt(i)); + Timer t = mHpjPkgTimers.get(userId, packages.valueAt(i)); + if (t != null) { + t.onStateChangedLocked(nowElapsed, isQuotaFree); + } + t = mPkgTimers.get(userId, packages.valueAt(i)); if (t != null) { t.onStateChangedLocked(nowElapsed, isQuotaFree); } @@ -1847,8 +2362,46 @@ public final class QuotaController extends StateController { } break; } + case MSG_PROCESS_USAGE_EVENT: { + final int userId = msg.arg1; + final UsageEvents.Event event = (UsageEvents.Event) msg.obj; + final String pkgName = event.getPackageName(); + switch (event.getEventType()) { + case UsageEvents.Event.ACTIVITY_RESUMED: + case UsageEvents.Event.ACTIVITY_PAUSED: + case UsageEvents.Event.ACTIVITY_STOPPED: + case UsageEvents.Event.ACTIVITY_DESTROYED: + synchronized (mLock) { + TopAppTimer timer = mTopAppTrackers.get(userId, pkgName); + if (timer == null) { + timer = new TopAppTimer(userId, pkgName); + mTopAppTrackers.add(userId, pkgName, timer); + } + timer.processEventLocked(event); + } + break; + case UsageEvents.Event.USER_INTERACTION: + case UsageEvents.Event.CHOOSER_ACTION: + case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + // Don't need to include SHORTCUT_INVOCATION. The app will be + // launched through it (if it's not already on top). + grantRewardForInstantEvent( + userId, pkgName, mHpjRewardInteractionMs); + break; + case UsageEvents.Event.NOTIFICATION_SEEN: + // Intentionally don't give too much for notification seen. + // Interactions will award more. + grantRewardForInstantEvent( + userId, pkgName, mHpjRewardNotificationSeenMs); + break; + } + + break; + } } } + + mIsProcessing = false; } } @@ -2030,6 +2583,7 @@ public final class QuotaController extends StateController { mQcConstants.mShouldReevaluateConstraints = false; mQcConstants.mRateLimitingConstantsUpdated = false; mQcConstants.mExecutionPeriodConstantsUpdated = false; + mQcConstants.mHpjLimitConstantsUpdated = false; } @Override @@ -2055,6 +2609,7 @@ public final class QuotaController extends StateController { private boolean mShouldReevaluateConstraints = false; private boolean mRateLimitingConstantsUpdated = false; private boolean mExecutionPeriodConstantsUpdated = false; + private boolean mHpjLimitConstantsUpdated = false; /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */ private static final String QC_CONSTANT_PREFIX = "qc_"; @@ -2128,6 +2683,36 @@ public final class QuotaController extends StateController { @VisibleForTesting static final String KEY_MIN_QUOTA_CHECK_DELAY_MS = QC_CONSTANT_PREFIX + "min_quota_check_delay_ms"; + @VisibleForTesting + static final String KEY_HPJ_LIMIT_ACTIVE_MS = + QC_CONSTANT_PREFIX + "hpj_limit_active_ms"; + @VisibleForTesting + static final String KEY_HPJ_LIMIT_WORKING_MS = + QC_CONSTANT_PREFIX + "hpj_limit_working_ms"; + @VisibleForTesting + static final String KEY_HPJ_LIMIT_FREQUENT_MS = + QC_CONSTANT_PREFIX + "hpj_limit_frequent_ms"; + @VisibleForTesting + static final String KEY_HPJ_LIMIT_RARE_MS = + QC_CONSTANT_PREFIX + "hpj_limit_rare_ms"; + @VisibleForTesting + static final String KEY_HPJ_LIMIT_RESTRICTED_MS = + QC_CONSTANT_PREFIX + "hpj_limit_restricted_ms"; + @VisibleForTesting + static final String KEY_HPJ_WINDOW_SIZE_MS = + QC_CONSTANT_PREFIX + "hpj_window_size_ms"; + @VisibleForTesting + static final String KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS = + QC_CONSTANT_PREFIX + "hpj_top_app_time_chunk_size_ms"; + @VisibleForTesting + static final String KEY_HPJ_REWARD_TOP_APP_MS = + QC_CONSTANT_PREFIX + "hpj_reward_top_app_ms"; + @VisibleForTesting + static final String KEY_HPJ_REWARD_INTERACTION_MS = + QC_CONSTANT_PREFIX + "hpj_reward_interaction_ms"; + @VisibleForTesting + static final String KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS = + QC_CONSTANT_PREFIX + "hpj_reward_notification_seen_ms"; private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS = 10 * 60 * 1000L; // 10 minutes @@ -2169,6 +2754,16 @@ public final class QuotaController extends StateController { private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20; private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds private static final long DEFAULT_MIN_QUOTA_CHECK_DELAY_MS = MINUTE_IN_MILLIS; + private static final long DEFAULT_HPJ_LIMIT_ACTIVE_MS = 30 * MINUTE_IN_MILLIS; + private static final long DEFAULT_HPJ_LIMIT_WORKING_MS = DEFAULT_HPJ_LIMIT_ACTIVE_MS; + private static final long DEFAULT_HPJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS; + private static final long DEFAULT_HPJ_LIMIT_RARE_MS = DEFAULT_HPJ_LIMIT_FREQUENT_MS; + private static final long DEFAULT_HPJ_LIMIT_RESTRICTED_MS = 5 * MINUTE_IN_MILLIS; + private static final long DEFAULT_HPJ_WINDOW_SIZE_MS = 24 * HOUR_IN_MILLIS; + private static final long DEFAULT_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS = 30 * SECOND_IN_MILLIS; + private static final long DEFAULT_HPJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS; + private static final long DEFAULT_HPJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS; + private static final long DEFAULT_HPJ_REWARD_NOTIFICATION_SEEN_MS = 0; /** How much time each app will have to run jobs within their standby bucket window. */ public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS; @@ -2328,6 +2923,74 @@ public final class QuotaController extends StateController { /** The minimum value that {@link #RATE_LIMITING_WINDOW_MS} can have. */ private static final long MIN_RATE_LIMITING_WINDOW_MS = 30 * SECOND_IN_MILLIS; + /** + * The total session limit of the particular standby bucket. Apps in this standby bucket + * can + * only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free + * HPJs). + */ + public long HPJ_LIMIT_ACTIVE_MS = DEFAULT_HPJ_LIMIT_ACTIVE_MS; + + /** + * The total session limit of the particular standby bucket. Apps in this standby bucket + * can + * only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free + * HPJs). + */ + public long HPJ_LIMIT_WORKING_MS = DEFAULT_HPJ_LIMIT_WORKING_MS; + + /** + * The total session limit of the particular standby bucket. Apps in this standby bucket + * can + * only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free + * HPJs). + */ + public long HPJ_LIMIT_FREQUENT_MS = DEFAULT_HPJ_LIMIT_FREQUENT_MS; + + /** + * The total session limit of the particular standby bucket. Apps in this standby bucket + * can + * only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free + * HPJs). + */ + public long HPJ_LIMIT_RARE_MS = DEFAULT_HPJ_LIMIT_RARE_MS; + + /** + * The total session limit of the particular standby bucket. Apps in this standby bucket + * can + * only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free + * HPJs). + */ + public long HPJ_LIMIT_RESTRICTED_MS = DEFAULT_HPJ_LIMIT_RESTRICTED_MS; + + /** + * The period of time used to calculate HPJ sessions. Apps can only have HPJ sessions + * totalling HPJ_LIMIT_<bucket>_MS within this period of time (without factoring in any + * rewards or free HPJs). + */ + public long HPJ_WINDOW_SIZE_MS = DEFAULT_HPJ_WINDOW_SIZE_MS; + + /** + * Length of time used to split an app's top time into chunks. + */ + public long HPJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS; + + /** + * How much HPJ quota to give back to an app based on the number of top app time chunks it + * had. + */ + public long HPJ_REWARD_TOP_APP_MS = DEFAULT_HPJ_REWARD_TOP_APP_MS; + + /** + * How much HPJ quota to give back to an app based on each non-top user interaction. + */ + public long HPJ_REWARD_INTERACTION_MS = DEFAULT_HPJ_REWARD_INTERACTION_MS; + + /** + * How much HPJ quota to give back to an app based on each notification seen event. + */ + public long HPJ_REWARD_NOTIFICATION_SEEN_MS = DEFAULT_HPJ_REWARD_NOTIFICATION_SEEN_MS; + public void processConstantLocked(@NonNull DeviceConfig.Properties properties, @NonNull String key) { switch (key) { @@ -2348,6 +3011,15 @@ public final class QuotaController extends StateController { updateRateLimitingConstantsLocked(); break; + case KEY_HPJ_LIMIT_ACTIVE_MS: + case KEY_HPJ_LIMIT_WORKING_MS: + case KEY_HPJ_LIMIT_FREQUENT_MS: + case KEY_HPJ_LIMIT_RARE_MS: + case KEY_HPJ_LIMIT_RESTRICTED_MS: + case KEY_HPJ_WINDOW_SIZE_MS: + updateHpjLimitConstantsLocked(); + break; + case KEY_MAX_JOB_COUNT_ACTIVE: MAX_JOB_COUNT_ACTIVE = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_ACTIVE); int newActiveMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE); @@ -2458,6 +3130,64 @@ public final class QuotaController extends StateController { mInQuotaAlarmListener.setMinQuotaCheckDelayMs( Math.min(15 * MINUTE_IN_MILLIS, Math.max(0, MIN_QUOTA_CHECK_DELAY_MS))); break; + case KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS: + // We don't need to re-evaluate execution stats or constraint status for this. + HPJ_TOP_APP_TIME_CHUNK_SIZE_MS = + properties.getLong(key, DEFAULT_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS); + // Limit chunking to be in the range [1 millisecond, 15 minutes] per event. + long newChunkSizeMs = Math.min(15 * MINUTE_IN_MILLIS, + Math.max(1, HPJ_TOP_APP_TIME_CHUNK_SIZE_MS)); + if (mHpjTopAppTimeChunkSizeMs != newChunkSizeMs) { + mHpjTopAppTimeChunkSizeMs = newChunkSizeMs; + if (mHpjTopAppTimeChunkSizeMs < mHpjRewardTopAppMs) { + // Not making chunk sizes and top rewards to be the upper/lower + // limits of the other to allow trying different policies. Just log + // the discrepancy. + Slog.w(TAG, "HPJ top app time chunk less than reward: " + + mHpjTopAppTimeChunkSizeMs + " vs " + mHpjRewardTopAppMs); + } + } + break; + case KEY_HPJ_REWARD_TOP_APP_MS: + // We don't need to re-evaluate execution stats or constraint status for this. + HPJ_REWARD_TOP_APP_MS = + properties.getLong(key, DEFAULT_HPJ_REWARD_TOP_APP_MS); + // Limit top reward to be in the range [10 seconds, 15 minutes] per event. + long newTopReward = Math.min(15 * MINUTE_IN_MILLIS, + Math.max(10 * SECOND_IN_MILLIS, HPJ_REWARD_TOP_APP_MS)); + if (mHpjRewardTopAppMs != newTopReward) { + mHpjRewardTopAppMs = newTopReward; + if (mHpjTopAppTimeChunkSizeMs < mHpjRewardTopAppMs) { + // Not making chunk sizes and top rewards to be the upper/lower + // limits of the other to allow trying different policies. Just log + // the discrepancy. + Slog.w(TAG, "HPJ top app time chunk less than reward: " + + mHpjTopAppTimeChunkSizeMs + " vs " + mHpjRewardTopAppMs); + } + } + break; + case KEY_HPJ_REWARD_INTERACTION_MS: + // We don't need to re-evaluate execution stats or constraint status for this. + HPJ_REWARD_INTERACTION_MS = + properties.getLong(key, DEFAULT_HPJ_REWARD_INTERACTION_MS); + // Limit interaction reward to be in the range [5 seconds, 15 minutes] per + // event. + long newInteractionReward = Math.min(15 * MINUTE_IN_MILLIS, + Math.max(5 * SECOND_IN_MILLIS, HPJ_REWARD_INTERACTION_MS)); + if (mHpjRewardInteractionMs != newInteractionReward) { + mHpjRewardInteractionMs = newInteractionReward; + } + break; + case KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS: + // We don't need to re-evaluate execution stats or constraint status for this. + HPJ_REWARD_NOTIFICATION_SEEN_MS = + properties.getLong(key, DEFAULT_HPJ_REWARD_NOTIFICATION_SEEN_MS); + // Limit notification seen reward to be in the range [0, 5] minutes per event. + long newNotiSeenReward = Math.min(5 * MINUTE_IN_MILLIS, + Math.max(0, HPJ_REWARD_NOTIFICATION_SEEN_MS)); + if (mHpjRewardNotificationSeenMs != newNotiSeenReward) { + mHpjRewardNotificationSeenMs = newNotiSeenReward; + } } } @@ -2598,6 +3328,75 @@ public final class QuotaController extends StateController { } } + private void updateHpjLimitConstantsLocked() { + if (mHpjLimitConstantsUpdated) { + return; + } + mHpjLimitConstantsUpdated = true; + + // Query the values as an atomic set. + final DeviceConfig.Properties properties = DeviceConfig.getProperties( + DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_HPJ_LIMIT_ACTIVE_MS, KEY_HPJ_LIMIT_WORKING_MS, + KEY_HPJ_LIMIT_FREQUENT_MS, KEY_HPJ_LIMIT_RARE_MS, + KEY_HPJ_LIMIT_RESTRICTED_MS, KEY_HPJ_WINDOW_SIZE_MS); + HPJ_LIMIT_ACTIVE_MS = properties.getLong( + KEY_HPJ_LIMIT_ACTIVE_MS, DEFAULT_HPJ_LIMIT_ACTIVE_MS); + HPJ_LIMIT_WORKING_MS = properties.getLong( + KEY_HPJ_LIMIT_WORKING_MS, DEFAULT_HPJ_LIMIT_WORKING_MS); + HPJ_LIMIT_FREQUENT_MS = properties.getLong( + KEY_HPJ_LIMIT_FREQUENT_MS, DEFAULT_HPJ_LIMIT_FREQUENT_MS); + HPJ_LIMIT_RARE_MS = properties.getLong( + KEY_HPJ_LIMIT_RARE_MS, DEFAULT_HPJ_LIMIT_RARE_MS); + HPJ_LIMIT_RESTRICTED_MS = properties.getLong( + KEY_HPJ_LIMIT_RESTRICTED_MS, DEFAULT_HPJ_LIMIT_RESTRICTED_MS); + HPJ_WINDOW_SIZE_MS = properties.getLong( + KEY_HPJ_WINDOW_SIZE_MS, DEFAULT_HPJ_WINDOW_SIZE_MS); + + // The window must be in the range [1 hour, 24 hours]. + long newWindowSizeMs = Math.max(HOUR_IN_MILLIS, + Math.min(MAX_PERIOD_MS, HPJ_WINDOW_SIZE_MS)); + if (mHpjLimitWindowSizeMs != newWindowSizeMs) { + mHpjLimitWindowSizeMs = newWindowSizeMs; + mShouldReevaluateConstraints = true; + } + // The limit must be in the range [15 minutes, window size]. + long newActiveLimitMs = Math.max(15 * MINUTE_IN_MILLIS, + Math.min(newWindowSizeMs, HPJ_LIMIT_ACTIVE_MS)); + if (mHpjLimitsMs[ACTIVE_INDEX] != newActiveLimitMs) { + mHpjLimitsMs[ACTIVE_INDEX] = newActiveLimitMs; + mShouldReevaluateConstraints = true; + } + // The limit must be in the range [15 minutes, active limit]. + long newWorkingLimitMs = Math.max(15 * MINUTE_IN_MILLIS, + Math.min(newActiveLimitMs, HPJ_LIMIT_WORKING_MS)); + if (mHpjLimitsMs[WORKING_INDEX] != newWorkingLimitMs) { + mHpjLimitsMs[WORKING_INDEX] = newWorkingLimitMs; + mShouldReevaluateConstraints = true; + } + // The limit must be in the range [10 minutes, working limit]. + long newFrequentLimitMs = Math.max(10 * MINUTE_IN_MILLIS, + Math.min(newWorkingLimitMs, HPJ_LIMIT_FREQUENT_MS)); + if (mHpjLimitsMs[FREQUENT_INDEX] != newFrequentLimitMs) { + mHpjLimitsMs[FREQUENT_INDEX] = newFrequentLimitMs; + mShouldReevaluateConstraints = true; + } + // The limit must be in the range [10 minutes, frequent limit]. + long newRareLimitMs = Math.max(10 * MINUTE_IN_MILLIS, + Math.min(newFrequentLimitMs, HPJ_LIMIT_RARE_MS)); + if (mHpjLimitsMs[RARE_INDEX] != newRareLimitMs) { + mHpjLimitsMs[RARE_INDEX] = newRareLimitMs; + mShouldReevaluateConstraints = true; + } + // The limit must be in the range [0 minutes, rare limit]. + long newRestrictedLimitMs = Math.max(0, + Math.min(newRareLimitMs, HPJ_LIMIT_RESTRICTED_MS)); + if (mHpjLimitsMs[RESTRICTED_INDEX] != newRestrictedLimitMs) { + mHpjLimitsMs[RESTRICTED_INDEX] = newRestrictedLimitMs; + mShouldReevaluateConstraints = true; + } + } + private void dump(IndentingPrintWriter pw) { pw.println(); pw.println("QuotaController:"); @@ -2628,6 +3427,19 @@ public final class QuotaController extends StateController { pw.print(KEY_TIMING_SESSION_COALESCING_DURATION_MS, TIMING_SESSION_COALESCING_DURATION_MS).println(); pw.print(KEY_MIN_QUOTA_CHECK_DELAY_MS, MIN_QUOTA_CHECK_DELAY_MS).println(); + + pw.print(KEY_HPJ_LIMIT_ACTIVE_MS, HPJ_LIMIT_ACTIVE_MS).println(); + pw.print(KEY_HPJ_LIMIT_WORKING_MS, HPJ_LIMIT_WORKING_MS).println(); + pw.print(KEY_HPJ_LIMIT_FREQUENT_MS, HPJ_LIMIT_FREQUENT_MS).println(); + pw.print(KEY_HPJ_LIMIT_RARE_MS, HPJ_LIMIT_RARE_MS).println(); + pw.print(KEY_HPJ_LIMIT_RESTRICTED_MS, HPJ_LIMIT_RESTRICTED_MS).println(); + pw.print(KEY_HPJ_WINDOW_SIZE_MS, HPJ_WINDOW_SIZE_MS).println(); + pw.print(KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS, HPJ_TOP_APP_TIME_CHUNK_SIZE_MS).println(); + pw.print(KEY_HPJ_REWARD_TOP_APP_MS, HPJ_REWARD_TOP_APP_MS).println(); + pw.print(KEY_HPJ_REWARD_INTERACTION_MS, HPJ_REWARD_INTERACTION_MS).println(); + pw.print(KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS, + HPJ_REWARD_NOTIFICATION_SEEN_MS).println(); + pw.decreaseIndent(); } @@ -2675,6 +3487,24 @@ public final class QuotaController extends StateController { TIMING_SESSION_COALESCING_DURATION_MS); proto.write(ConstantsProto.QuotaController.MIN_QUOTA_CHECK_DELAY_MS, MIN_QUOTA_CHECK_DELAY_MS); + + proto.write(ConstantsProto.QuotaController.HPJ_LIMIT_ACTIVE_MS, HPJ_LIMIT_ACTIVE_MS); + proto.write(ConstantsProto.QuotaController.HPJ_LIMIT_WORKING_MS, HPJ_LIMIT_WORKING_MS); + proto.write(ConstantsProto.QuotaController.HPJ_LIMIT_FREQUENT_MS, + HPJ_LIMIT_FREQUENT_MS); + proto.write(ConstantsProto.QuotaController.HPJ_LIMIT_RARE_MS, HPJ_LIMIT_RARE_MS); + proto.write(ConstantsProto.QuotaController.HPJ_LIMIT_RESTRICTED_MS, + HPJ_LIMIT_RESTRICTED_MS); + proto.write(ConstantsProto.QuotaController.HPJ_WINDOW_SIZE_MS, HPJ_WINDOW_SIZE_MS); + proto.write(ConstantsProto.QuotaController.HPJ_TOP_APP_TIME_CHUNK_SIZE_MS, + HPJ_TOP_APP_TIME_CHUNK_SIZE_MS); + proto.write(ConstantsProto.QuotaController.HPJ_REWARD_TOP_APP_MS, + HPJ_REWARD_TOP_APP_MS); + proto.write(ConstantsProto.QuotaController.HPJ_REWARD_INTERACTION_MS, + HPJ_REWARD_INTERACTION_MS); + proto.write(ConstantsProto.QuotaController.HPJ_REWARD_NOTIFICATION_SEEN_MS, + HPJ_REWARD_NOTIFICATION_SEEN_MS); + proto.end(qcToken); } } @@ -2717,6 +3547,49 @@ public final class QuotaController extends StateController { } @VisibleForTesting + @NonNull + long[] getHpjLimitsMs() { + return mHpjLimitsMs; + } + + @VisibleForTesting + @NonNull + long getHpjLimitWindowSizeMs() { + return mHpjLimitWindowSizeMs; + } + + @VisibleForTesting + @NonNull + long getHpjRewardInteractionMs() { + return mHpjRewardInteractionMs; + } + + @VisibleForTesting + @NonNull + long getHpjRewardNotificationSeenMs() { + return mHpjRewardNotificationSeenMs; + } + + @VisibleForTesting + @NonNull + long getHpjRewardTopAppMs() { + return mHpjRewardTopAppMs; + } + + + @VisibleForTesting + @Nullable + List<TimingSession> getHpjTimingSessions(int userId, String packageName) { + return mHpjTimingSessions.get(userId, packageName); + } + + @VisibleForTesting + @NonNull + long getHpjTopAppTimeChunkSizeMs() { + return mHpjTopAppTimeChunkSizeMs; + } + + @VisibleForTesting long getInQuotaBufferMs() { return mQuotaBufferMs; } @@ -2763,6 +3636,11 @@ public final class QuotaController extends StateController { return mQcConstants; } + @VisibleForTesting + boolean isActiveBackgroundProcessing() { + return mHandler.mIsProcessing; + } + //////////////////////////// DATA DUMP ////////////////////////////// @Override @@ -2805,16 +3683,24 @@ public final class QuotaController extends StateController { pw.increaseIndent(); pw.print(JobStatus.bucketName(js.getEffectiveStandbyBucket())); pw.print(", "); - if (js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) { - pw.print("within quota"); + if (js.shouldTreatAsForegroundJob()) { + pw.print("within HPJ quota"); + } else if (js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) { + pw.print("within regular quota"); } else { pw.print("not within quota"); } pw.print(", "); - pw.print(getRemainingExecutionTimeLocked(js)); - pw.print("ms remaining in quota"); - pw.decreaseIndent(); + if (js.shouldTreatAsForegroundJob()) { + pw.print(getRemainingHpjExecutionTimeLocked( + js.getSourceUserId(), js.getSourcePackageName())); + pw.print("ms remaining in HPJ quota"); + } else { + pw.print(getRemainingExecutionTimeLocked(js)); + pw.print("ms remaining in quota"); + } pw.println(); + pw.decreaseIndent(); } }); @@ -2841,6 +3727,33 @@ public final class QuotaController extends StateController { } } + pw.println(); + for (int u = 0; u < mHpjPkgTimers.numMaps(); ++u) { + final int userId = mHpjPkgTimers.keyAt(u); + for (int p = 0; p < mHpjPkgTimers.numElementsForKey(userId); ++p) { + final String pkgName = mHpjPkgTimers.keyAt(u, p); + mHpjPkgTimers.valueAt(u, p).dump(pw, predicate); + pw.println(); + List<TimingSession> sessions = mHpjTimingSessions.get(userId, pkgName); + if (sessions != null) { + pw.increaseIndent(); + pw.println("Saved sessions:"); + pw.increaseIndent(); + for (int j = sessions.size() - 1; j >= 0; j--) { + TimingSession session = sessions.get(j); + session.dump(pw); + } + pw.decreaseIndent(); + pw.decreaseIndent(); + pw.println(); + } + } + } + + pw.println(); + mTopAppTrackers.forEach((timer) -> timer.dump(pw)); + + pw.println(); pw.println("Cached execution stats:"); pw.increaseIndent(); for (int u = 0; u < mExecutionStatsCache.numMaps(); ++u) { @@ -2865,6 +3778,22 @@ public final class QuotaController extends StateController { pw.decreaseIndent(); pw.println(); + pw.println("HPJ debits:"); + pw.increaseIndent(); + for (int u = 0; u < mHpjStats.numMaps(); ++u) { + final int userId = mHpjStats.keyAt(u); + for (int p = 0; p < mHpjStats.numElementsForKey(userId); ++p) { + final String pkgName = mHpjStats.keyAt(u, p); + ShrinkableDebits debits = mHpjStats.valueAt(u, p); + + pw.print(string(userId, pkgName)); + pw.print(": "); + debits.dumpLocked(pw); + } + } + pw.decreaseIndent(); + + pw.println(); mInQuotaAlarmListener.dumpLocked(pw); pw.decreaseIndent(); } @@ -2919,6 +3848,12 @@ public final class QuotaController extends StateController { js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); proto.write(StateControllerProto.QuotaController.TrackedJob.REMAINING_QUOTA_MS, getRemainingExecutionTimeLocked(js)); + proto.write( + StateControllerProto.QuotaController.TrackedJob.IS_REQUESTED_FOREGROUND_JOB, + js.isRequestedForegroundJob()); + proto.write( + StateControllerProto.QuotaController.TrackedJob.IS_WITHIN_FG_JOB_QUOTA, + js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); proto.end(jsToken); } }); @@ -2929,8 +3864,15 @@ public final class QuotaController extends StateController { final String pkgName = mPkgTimers.keyAt(u, p); final long psToken = proto.start( StateControllerProto.QuotaController.PACKAGE_STATS); + mPkgTimers.valueAt(u, p).dump(proto, StateControllerProto.QuotaController.PackageStats.TIMER, predicate); + final Timer hpjTimer = mHpjPkgTimers.get(userId, pkgName); + if (hpjTimer != null) { + hpjTimer.dump(proto, + StateControllerProto.QuotaController.PackageStats.FG_JOB_TIMER, + predicate); + } List<TimingSession> sessions = mTimingSessions.get(userId, pkgName); if (sessions != null) { diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp index bf4323ddfb0b..e4299f52ff7d 100644 --- a/apex/statsd/framework/Android.bp +++ b/apex/statsd/framework/Android.bp @@ -45,6 +45,7 @@ filegroup { visibility: [ "//frameworks/base", // For the "global" stubs. "//frameworks/base/apex/statsd:__subpackages__", + "//packages/modules/StatsD/apex:__subpackages__", ], } java_sdk_library { @@ -72,7 +73,10 @@ java_sdk_library { hostdex: true, // for hiddenapi check - impl_library_visibility: ["//frameworks/base/apex/statsd/framework/test:__subpackages__"], + impl_library_visibility: [ + "//frameworks/base/apex/statsd/framework/test:__subpackages__", + "//packages/modules/StatsD/apex/framework/test:__subpackages__", + ], apex_available: [ "com.android.os.statsd", diff --git a/cmds/statsd/.clang-format b/cmds/statsd/.clang-format deleted file mode 100644 index cead3a079435..000000000000 --- a/cmds/statsd/.clang-format +++ /dev/null @@ -1,17 +0,0 @@ -BasedOnStyle: Google -AllowShortIfStatementsOnASingleLine: true -AllowShortFunctionsOnASingleLine: false -AllowShortLoopsOnASingleLine: true -BinPackArguments: true -BinPackParameters: true -ColumnLimit: 100 -CommentPragmas: NOLINT:.* -ContinuationIndentWidth: 8 -DerivePointerAlignment: false -IndentWidth: 4 -PointerAlignment: Left -TabWidth: 4 -AccessModifierOffset: -4 -IncludeCategories: - - Regex: '^"Log\.h"' - Priority: -1 diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp deleted file mode 100644 index 6aad82fda915..000000000000 --- a/cmds/statsd/Android.bp +++ /dev/null @@ -1,444 +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. -// - -cc_defaults { - name: "statsd_defaults", - - srcs: [ - "src/active_config_list.proto", - "src/anomaly/AlarmMonitor.cpp", - "src/anomaly/AlarmTracker.cpp", - "src/anomaly/AnomalyTracker.cpp", - "src/anomaly/DurationAnomalyTracker.cpp", - "src/anomaly/subscriber_util.cpp", - "src/condition/CombinationConditionTracker.cpp", - "src/condition/condition_util.cpp", - "src/condition/ConditionWizard.cpp", - "src/condition/SimpleConditionTracker.cpp", - "src/config/ConfigKey.cpp", - "src/config/ConfigListener.cpp", - "src/config/ConfigManager.cpp", - "src/experiment_ids.proto", - "src/external/Perfetto.cpp", - "src/external/PullResultReceiver.cpp", - "src/external/puller_util.cpp", - "src/external/StatsCallbackPuller.cpp", - "src/external/StatsPuller.cpp", - "src/external/StatsPullerManager.cpp", - "src/external/TrainInfoPuller.cpp", - "src/FieldValue.cpp", - "src/guardrail/StatsdStats.cpp", - "src/hash.cpp", - "src/HashableDimensionKey.cpp", - "src/logd/LogEvent.cpp", - "src/logd/LogEventQueue.cpp", - "src/matchers/CombinationAtomMatchingTracker.cpp", - "src/matchers/EventMatcherWizard.cpp", - "src/matchers/matcher_util.cpp", - "src/matchers/SimpleAtomMatchingTracker.cpp", - "src/metadata_util.cpp", - "src/metrics/CountMetricProducer.cpp", - "src/metrics/duration_helper/MaxDurationTracker.cpp", - "src/metrics/duration_helper/OringDurationTracker.cpp", - "src/metrics/DurationMetricProducer.cpp", - "src/metrics/EventMetricProducer.cpp", - "src/metrics/GaugeMetricProducer.cpp", - "src/metrics/MetricProducer.cpp", - "src/metrics/MetricsManager.cpp", - "src/metrics/parsing_utils/config_update_utils.cpp", - "src/metrics/parsing_utils/metrics_manager_util.cpp", - "src/metrics/ValueMetricProducer.cpp", - "src/packages/UidMap.cpp", - "src/shell/shell_config.proto", - "src/shell/ShellSubscriber.cpp", - "src/socket/StatsSocketListener.cpp", - "src/state/StateManager.cpp", - "src/state/StateTracker.cpp", - "src/stats_log_util.cpp", - "src/statscompanion_util.cpp", - "src/statsd_config.proto", - "src/statsd_metadata.proto", - "src/StatsLogProcessor.cpp", - "src/StatsService.cpp", - "src/storage/StorageManager.cpp", - "src/subscriber/IncidentdReporter.cpp", - "src/subscriber/SubscriberReporter.cpp", - "src/uid_data.proto", - "src/utils/MultiConditionTrigger.cpp", - ], - - local_include_dirs: [ - "src", - ], - - static_libs: [ - "libbase", - "libcutils", - "libgtest_prod", - "libprotoutil", - "libstatslog_statsd", - "libsysutils", - "libutils", - "statsd-aidl-ndk_platform", - ], - shared_libs: [ - "libbinder_ndk", - "libincident", - "liblog", - ], -} - -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", - ], -} - -genrule { - name: "statslog_statsdtest.h", - tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsdtest.h --module statsdtest --namespace android,os,statsd,util", - out: [ - "statslog_statsdtest.h", - ], -} - -genrule { - name: "statslog_statsdtest.cpp", - tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsdtest.cpp --module statsdtest --namespace android,os,statsd,util --importHeader statslog_statsdtest.h", - out: [ - "statslog_statsdtest.cpp", - ], -} - -cc_library_static { - name: "libstatslog_statsdtest", - generated_sources: ["statslog_statsdtest.cpp"], - generated_headers: ["statslog_statsdtest.h"], - export_generated_headers: ["statslog_statsdtest.h"], - shared_libs: [ - "libstatssocket", - "libstatspull", - ], -} - -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", - "libstatspull", - ], - export_shared_lib_headers: [ - "libstatspull", - ], -} - -// ========= -// statsd -// ========= - -cc_binary { - name: "statsd", - defaults: ["statsd_defaults"], - - srcs: ["src/main.cpp"], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - "-Wno-unused-parameter", - // optimize for size (protobuf glop can get big) - "-Os", - // "-g", - // "-O0", - ], - - product_variables: { - eng: { - // Enable sanitizer ONLY on eng builds - //sanitize: { - // address: true, - //}, - }, - }, - - proto: { - type: "lite", - static: true, - }, - stl: "libc++_static", - - shared_libs: [ - "libstatssocket", - ], - - apex_available: [ - "com.android.os.statsd", - "test_com.android.os.statsd", - ], -} - -// ============== -// statsd_test -// ============== - -cc_test { - name: "statsd_test", - defaults: ["statsd_defaults"], - test_suites: ["device-tests", "mts"], - test_config: "statsd_test.xml", - - //TODO(b/153588990): Remove when the build system properly separates - //32bit and 64bit architectures. - compile_multilib: "both", - multilib: { - lib64: { - suffix: "64", - }, - lib32: { - suffix: "32", - }, - }, - - cflags: [ - "-Wall", - "-Werror", - "-Wno-missing-field-initializers", - "-Wno-unused-variable", - "-Wno-unused-function", - "-Wno-unused-parameter", - ], - - require_root: true, - - srcs: [ - // atom_field_options.proto needs field_options.proto, but that is - // not included in libprotobuf-cpp-lite, so compile it here. - ":libprotobuf-internal-protos", - ":libstats_internal_protos", - - "src/shell/shell_data.proto", - "src/stats_log.proto", - "tests/AlarmMonitor_test.cpp", - "tests/anomaly/AlarmTracker_test.cpp", - "tests/anomaly/AnomalyTracker_test.cpp", - "tests/condition/CombinationConditionTracker_test.cpp", - "tests/condition/ConditionTimer_test.cpp", - "tests/condition/SimpleConditionTracker_test.cpp", - "tests/ConfigManager_test.cpp", - "tests/e2e/Alarm_e2e_test.cpp", - "tests/e2e/Anomaly_count_e2e_test.cpp", - "tests/e2e/Anomaly_duration_sum_e2e_test.cpp", - "tests/e2e/Attribution_e2e_test.cpp", - "tests/e2e/ConfigTtl_e2e_test.cpp", - "tests/e2e/ConfigUpdate_e2e_test.cpp", - "tests/e2e/CountMetric_e2e_test.cpp", - "tests/e2e/DurationMetric_e2e_test.cpp", - "tests/e2e/GaugeMetric_e2e_pull_test.cpp", - "tests/e2e/GaugeMetric_e2e_push_test.cpp", - "tests/e2e/MetricActivation_e2e_test.cpp", - "tests/e2e/MetricConditionLink_e2e_test.cpp", - "tests/e2e/PartialBucket_e2e_test.cpp", - "tests/e2e/ValueMetric_pull_e2e_test.cpp", - "tests/e2e/WakelockDuration_e2e_test.cpp", - "tests/external/puller_util_test.cpp", - "tests/external/StatsCallbackPuller_test.cpp", - "tests/external/StatsPuller_test.cpp", - "tests/external/StatsPullerManager_test.cpp", - "tests/FieldValue_test.cpp", - "tests/guardrail/StatsdStats_test.cpp", - "tests/HashableDimensionKey_test.cpp", - "tests/indexed_priority_queue_test.cpp", - "tests/log_event/LogEventQueue_test.cpp", - "tests/LogEntryMatcher_test.cpp", - "tests/LogEvent_test.cpp", - "tests/metadata_util_test.cpp", - "tests/metrics/CountMetricProducer_test.cpp", - "tests/metrics/DurationMetricProducer_test.cpp", - "tests/metrics/EventMetricProducer_test.cpp", - "tests/metrics/GaugeMetricProducer_test.cpp", - "tests/metrics/MaxDurationTracker_test.cpp", - "tests/metrics/metrics_test_helper.cpp", - "tests/metrics/OringDurationTracker_test.cpp", - "tests/metrics/ValueMetricProducer_test.cpp", - "tests/metrics/parsing_utils/config_update_utils_test.cpp", - "tests/metrics/parsing_utils/metrics_manager_util_test.cpp", - "tests/MetricsManager_test.cpp", - "tests/shell/ShellSubscriber_test.cpp", - "tests/state/StateTracker_test.cpp", - "tests/statsd_test_util.cpp", - "tests/StatsLogProcessor_test.cpp", - "tests/StatsService_test.cpp", - "tests/storage/StorageManager_test.cpp", - "tests/UidMap_test.cpp", - "tests/utils/MultiConditionTrigger_test.cpp", - ], - - static_libs: [ - "libgmock", - "libplatformprotos", - "libstatslog_statsdtest", - "libstatssocket_private", - ], - - proto: { - type: "lite", - include_dirs: [ - "external/protobuf/src", - "frameworks/proto_logging/stats", - ], - }, - -} - -//############################# -// statsd micro benchmark -//############################# - -cc_benchmark { - name: "statsd_benchmark", - defaults: ["statsd_defaults"], - - srcs: [ - // atom_field_options.proto needs field_options.proto, but that is - // not included in libprotobuf-cpp-lite, so compile it here. - ":libprotobuf-internal-protos", - ":libstats_internal_protos", - - "benchmark/duration_metric_benchmark.cpp", - "benchmark/filter_value_benchmark.cpp", - "benchmark/get_dimensions_for_condition_benchmark.cpp", - "benchmark/hello_world_benchmark.cpp", - "benchmark/log_event_benchmark.cpp", - "benchmark/main.cpp", - "benchmark/metric_util.cpp", - "benchmark/stats_write_benchmark.cpp", - "src/stats_log.proto", - ], - - proto: { - type: "lite", - include_dirs: [ - "external/protobuf/src", - "frameworks/proto_logging/stats", - ], - }, - - cflags: [ - "-Wall", - "-Werror", - "-Wno-unused-parameter", - "-Wno-unused-variable", - "-Wno-unused-function", - - // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374 - "-Wno-varargs", - ], - - static_libs: [ - "libplatformprotos", - "libstatssocket_private", - ], - - shared_libs: [ - "libgtest_prod", - "libprotobuf-cpp-lite", - "libstatslog", - ], -} - -// ==== java proto device library (for test only) ============================== -java_library { - name: "statsdprotolite", - sdk_version: "core_current", - proto: { - type: "lite", - include_dirs: [ - "external/protobuf/src", - "frameworks/proto_logging/stats", - ], - }, - - srcs: [ - ":libstats_atoms_proto", - "src/shell/shell_config.proto", - "src/shell/shell_data.proto", - "src/stats_log.proto", - "src/statsd_config.proto", - ], - - static_libs: [ - "platformprotoslite", - ], - // Protos have lots of MissingOverride and similar. - errorprone: { - javacflags: ["-XepDisableAllChecks"], - }, -} - -java_library { - name: "statsdprotonano", - sdk_version: "9", - proto: { - type: "nano", - output_params: ["store_unknown_fields=true"], - include_dirs: [ - "external/protobuf/src", - "frameworks/proto_logging/stats", - ], - }, - srcs: [ - ":libstats_atoms_proto", - "src/shell/shell_config.proto", - "src/shell/shell_data.proto", - "src/stats_log.proto", - "src/statsd_config.proto", - ], - static_libs: [ - "platformprotosnano", - ], - // Protos have lots of MissingOverride and similar. - errorprone: { - javacflags: ["-XepDisableAllChecks"], - }, -} - -// Filegroup for statsd config proto definition. -filegroup { - name: "statsd-config-proto-def", - srcs: ["src/statsd_config.proto"], -} diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS deleted file mode 100644 index 4e4e11988b62..000000000000 --- a/cmds/statsd/OWNERS +++ /dev/null @@ -1 +0,0 @@ -baligh@google.com diff --git a/cmds/statsd/TEST_MAPPING b/cmds/statsd/TEST_MAPPING deleted file mode 100644 index a7a4cf14182e..000000000000 --- a/cmds/statsd/TEST_MAPPING +++ /dev/null @@ -1,17 +0,0 @@ -{ - "presubmit" : [ - { - "name" : "statsd_test" - } - ], - - "postsubmit" : [ - { - "name" : "CtsStatsdHostTestCases" - }, - { - "name" : "GtsStatsdHostTestCases" - } - ] - -} diff --git a/cmds/statsd/benchmark/duration_metric_benchmark.cpp b/cmds/statsd/benchmark/duration_metric_benchmark.cpp deleted file mode 100644 index 2d315d9395bc..000000000000 --- a/cmds/statsd/benchmark/duration_metric_benchmark.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * 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. - */ -#include <vector> -#include "benchmark/benchmark.h" -#include "FieldValue.h" -#include "HashableDimensionKey.h" -#include "logd/LogEvent.h" -#include "stats_log_util.h" -#include "metric_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -static StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition( - DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) { - StatsdConfig config; - *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); - *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); - *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); - *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto scheduledJobPredicate = CreateScheduledJobPredicate(); - auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); - dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED); - dimensions->add_child()->set_field(2); // job name field. - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - - auto isSyncingPredicate = CreateIsSyncingPredicate(); - auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, - {Position::FIRST}); - if (addExtraDimensionInCondition) { - syncDimension->add_child()->set_field(2 /* name field*/); - } - - *config.add_predicate() = scheduledJobPredicate; - *config.add_predicate() = screenIsOffPredicate; - *config.add_predicate() = isSyncingPredicate; - auto combinationPredicate = config.add_predicate(); - combinationPredicate->set_id(StringToId("CombinationPredicate")); - combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); - addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); - - auto metric = config.add_duration_metric(); - metric->set_bucket(FIVE_MINUTES); - metric->set_id(StringToId("scheduledJob")); - metric->set_what(scheduledJobPredicate.id()); - metric->set_condition(combinationPredicate->id()); - metric->set_aggregation_type(aggregationType); - auto dimensionWhat = metric->mutable_dimensions_in_what(); - dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED); - dimensionWhat->add_child()->set_field(2); // job name field. - *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); - return config; -} - -static StatsdConfig CreateDurationMetricConfig_Link_AND_CombinationCondition( - DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) { - StatsdConfig config; - *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); - *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); - *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); - *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto scheduledJobPredicate = CreateScheduledJobPredicate(); - auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); - *dimensions = CreateAttributionUidDimensions( - android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); - dimensions->add_child()->set_field(2); // job name field. - - auto isSyncingPredicate = CreateIsSyncingPredicate(); - auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); - if (addExtraDimensionInCondition) { - syncDimension->add_child()->set_field(2 /* name field*/); - } - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - - *config.add_predicate() = scheduledJobPredicate; - *config.add_predicate() = screenIsOffPredicate; - *config.add_predicate() = isSyncingPredicate; - auto combinationPredicate = config.add_predicate(); - combinationPredicate->set_id(StringToId("CombinationPredicate")); - combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); - addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); - - auto metric = config.add_duration_metric(); - metric->set_bucket(FIVE_MINUTES); - metric->set_id(StringToId("scheduledJob")); - metric->set_what(scheduledJobPredicate.id()); - metric->set_condition(combinationPredicate->id()); - metric->set_aggregation_type(aggregationType); - *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( - android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); - - auto links = metric->add_links(); - links->set_condition(isSyncingPredicate.id()); - *links->mutable_fields_in_what() = - CreateAttributionUidDimensions( - android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); - *links->mutable_fields_in_condition() = - CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); - return config; -} - -static void BM_DurationMetricNoLink(benchmark::State& state) { - ConfigKey cfgKey; - auto config = CreateDurationMetricConfig_NoLink_AND_CombinationCondition( - DurationMetric::SUM, false); - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - - std::vector<std::unique_ptr<LogEvent>> events; - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 11, - android::view::DISPLAY_STATE_OFF)); - events.push_back( - CreateScreenStateChangedEvent(bucketStartTimeNs + 40, android::view::DISPLAY_STATE_ON)); - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 102, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450, - android::view::DISPLAY_STATE_ON)); - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 650, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100, - android::view::DISPLAY_STATE_ON)); - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 640, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 650, - android::view::DISPLAY_STATE_ON)); - - vector<int> attributionUids1 = {9999}; - vector<string> attributionTags1 = {""}; - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 2, attributionUids1, - attributionTags1, "job0")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1, - attributionTags1, "job0")); - - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids1, - attributionTags1, "job2")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids1, - attributionTags1, "job2")); - - vector<int> attributionUids2 = {8888}; - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2, - attributionTags1, "job2")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850, - attributionUids2, attributionTags1, "job2")); - - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 600, - attributionUids2, attributionTags1, "job1")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900, - attributionUids2, attributionTags1, "job1")); - - vector<int> attributionUids3 = {111, 222, 222}; - vector<string> attributionTags3 = {"App1", "GMSCoreModule1", "GMSCoreModule2"}; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 10, attributionUids3, - attributionTags3, "ReadEmail")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 50, attributionUids3, attributionTags3, - "ReadEmail")); - - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200, attributionUids3, - attributionTags3, "ReadEmail")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids3, - attributionTags3, "ReadEmail")); - - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids3, - attributionTags3, "ReadDoc")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids3, - attributionTags3, "ReadDoc")); - - vector<int> attributionUids4 = {333, 222, 555}; - vector<string> attributionTags4 = {"App2", "GMSCoreModule1", "GMSCoreModule2"}; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 401, attributionUids4, - attributionTags4, "ReadEmail")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids4, - attributionTags4, "ReadEmail")); - sortLogEventsByTimestamp(&events); - - while (state.KeepRunning()) { - auto processor = CreateStatsLogProcessor( - bucketStartTimeNs / NS_PER_SEC, config, cfgKey); - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - } -} - -BENCHMARK(BM_DurationMetricNoLink); - - -static void BM_DurationMetricLink(benchmark::State& state) { - ConfigKey cfgKey; - auto config = CreateDurationMetricConfig_Link_AND_CombinationCondition( - DurationMetric::SUM, false); - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - - std::vector<std::unique_ptr<LogEvent>> events; - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 55, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 120, - android::view::DISPLAY_STATE_ON)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 121, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450, - android::view::DISPLAY_STATE_ON)); - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 501, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100, - android::view::DISPLAY_STATE_ON)); - - vector<int> attributionUids1 = {111}; - vector<string> attributionTags1 = {"App1"}; - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 1, attributionUids1, - attributionTags1, "job1")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1, - attributionTags1, "job1")); - - vector<int> attributionUids2 = {333}; - vector<string> attributionTags2 = {"App2"}; - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids2, - attributionTags2, "job2")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids2, - attributionTags2, "job2")); - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2, - attributionTags2, "job2")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850, - attributionUids2, attributionTags2, "job2")); - - vector<int> attributionUids3 = {444}; - vector<string> attributionTags3 = {"App3"}; - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs - 2, - attributionUids3, attributionTags3, "job3")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900, - attributionUids3, attributionTags3, "job3")); - - vector<int> attributionUids4 = {111, 222, 222}; - vector<string> attributionTags4 = {"App1", "GMSCoreModule1", "GMSCoreModule2"}; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids4, - attributionTags4, "ReadEmail")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 110, attributionUids4, attributionTags4, - "ReadEmail")); - - vector<int> attributionUids5 = {333, 222, 555}; - vector<string> attributionTags5 = {"App2", "GMSCoreModule1", "GMSCoreModule2"}; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 300, attributionUids5, - attributionTags5, "ReadEmail")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids5, - attributionTags5, "ReadEmail")); - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids5, - attributionTags5, "ReadDoc")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids5, - attributionTags5, "ReadDoc")); - - vector<int> attributionUids6 = {444, 222, 555}; - vector<string> attributionTags6 = {"App3", "GMSCoreModule1", "GMSCoreModule2"}; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 550, attributionUids6, - attributionTags6, "ReadDoc")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 800, attributionUids6, attributionTags6, - "ReadDoc")); - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids6, - attributionTags6, "ReadDoc")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids6, - attributionTags6, "ReadDoc")); - sortLogEventsByTimestamp(&events); - - while (state.KeepRunning()) { - auto processor = CreateStatsLogProcessor( - bucketStartTimeNs / NS_PER_SEC, config, cfgKey); - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - } -} - -BENCHMARK(BM_DurationMetricLink); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp deleted file mode 100644 index 743ccc4ed451..000000000000 --- a/cmds/statsd/benchmark/filter_value_benchmark.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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. - */ -#include <vector> - -#include "FieldValue.h" -#include "HashableDimensionKey.h" -#include "benchmark/benchmark.h" -#include "logd/LogEvent.h" -#include "metric_util.h" -#include "stats_event.h" -#include "stats_log_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -static void createLogEventAndMatcher(LogEvent* event, FieldMatcher* field_matcher) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 1); - AStatsEvent_overwriteTimestamp(statsEvent, 100000); - - std::vector<int> attributionUids = {100, 100}; - std::vector<string> attributionTags = {"LOCATION", "LOCATION"}; - writeAttribution(statsEvent, attributionUids, attributionTags); - - AStatsEvent_writeFloat(statsEvent, 3.2f); - AStatsEvent_writeString(statsEvent, "LOCATION"); - AStatsEvent_writeInt64(statsEvent, 990); - - parseStatsEventToLogEvent(statsEvent, event); - - field_matcher->set_field(1); - auto child = field_matcher->add_child(); - child->set_field(1); - child->set_position(FIRST); - child->add_child()->set_field(1); -} - -static void BM_FilterValue(benchmark::State& state) { - LogEvent event(/*uid=*/0, /*pid=*/0); - FieldMatcher field_matcher; - createLogEventAndMatcher(&event, &field_matcher); - - std::vector<Matcher> matchers; - translateFieldMatcher(field_matcher, &matchers); - - while (state.KeepRunning()) { - HashableDimensionKey output; - filterValues(matchers, event.getValues(), &output); - } -} - -BENCHMARK(BM_FilterValue); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp deleted file mode 100644 index 7a455650a31b..000000000000 --- a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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. - */ -#include <vector> - -#include "FieldValue.h" -#include "HashableDimensionKey.h" -#include "benchmark/benchmark.h" -#include "logd/LogEvent.h" -#include "metric_util.h" -#include "stats_event.h" -#include "stats_log_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 1); - AStatsEvent_overwriteTimestamp(statsEvent, 100000); - - std::vector<int> attributionUids = {100, 100}; - std::vector<string> attributionTags = {"LOCATION", "LOCATION"}; - writeAttribution(statsEvent, attributionUids, attributionTags); - - AStatsEvent_writeFloat(statsEvent, 3.2f); - AStatsEvent_writeString(statsEvent, "LOCATION"); - AStatsEvent_writeInt64(statsEvent, 990); - - parseStatsEventToLogEvent(statsEvent, event); - - link->conditionId = 1; - - FieldMatcher field_matcher; - field_matcher.set_field(event->GetTagId()); - auto child = field_matcher.add_child(); - child->set_field(1); - child->set_position(FIRST); - child->add_child()->set_field(1); - - translateFieldMatcher(field_matcher, &link->metricFields); - field_matcher.set_field(event->GetTagId() + 1); - translateFieldMatcher(field_matcher, &link->conditionFields); -} - -static void BM_GetDimensionInCondition(benchmark::State& state) { - Metric2Condition link; - LogEvent event(/*uid=*/0, /*pid=*/0); - createLogEventAndLink(&event, &link); - - while (state.KeepRunning()) { - HashableDimensionKey output; - getDimensionForCondition(event.getValues(), link, &output); - } -} - -BENCHMARK(BM_GetDimensionInCondition); - - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/hello_world_benchmark.cpp b/cmds/statsd/benchmark/hello_world_benchmark.cpp deleted file mode 100644 index c732d394bf64..000000000000 --- a/cmds/statsd/benchmark/hello_world_benchmark.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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. - */ -#include "benchmark/benchmark.h" - -static void BM_StringCreation(benchmark::State& state) { - while (state.KeepRunning()) std::string empty_string; -} -// Register the function as a benchmark -BENCHMARK(BM_StringCreation); - -// Define another benchmark -static void BM_StringCopy(benchmark::State& state) { - std::string x = "hello"; - while (state.KeepRunning()) std::string copy(x); -} -BENCHMARK(BM_StringCopy); diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp deleted file mode 100644 index 057e00bde202..000000000000 --- a/cmds/statsd/benchmark/log_event_benchmark.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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. - */ -#include <vector> -#include "benchmark/benchmark.h" -#include "logd/LogEvent.h" -#include "stats_event.h" - -namespace android { -namespace os { -namespace statsd { - -static size_t createAndParseStatsEvent(uint8_t* msg) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - AStatsEvent_writeInt32(event, 2); - AStatsEvent_writeFloat(event, 2.0); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - memcpy(msg, buf, size); - return size; -} - -static void BM_LogEventCreation(benchmark::State& state) { - uint8_t msg[LOGGER_ENTRY_MAX_PAYLOAD]; - size_t size = createAndParseStatsEvent(msg); - while (state.KeepRunning()) { - LogEvent event(/*uid=*/ 1000, /*pid=*/ 1001); - benchmark::DoNotOptimize(event.parseBuffer(msg, size)); - } -} -BENCHMARK(BM_LogEventCreation); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp deleted file mode 100644 index 89fd3d9b29ab..000000000000 --- a/cmds/statsd/benchmark/metric_util.cpp +++ /dev/null @@ -1,379 +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. - -#include "metric_util.h" - -#include "stats_event.h" - -namespace android { -namespace os { -namespace statsd { - -AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(atomId); - return atom_matcher; -} - -AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name, - ScheduledJobStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(3); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateStartScheduledJobAtomMatcher() { - return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart", - ScheduledJobStateChanged::STARTED); -} - -AtomMatcher CreateFinishScheduledJobAtomMatcher() { - return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish", - ScheduledJobStateChanged::FINISHED); -} - -AtomMatcher CreateScreenBrightnessChangedAtomMatcher() { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId("ScreenBrightnessChanged")); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED); - return atom_matcher; -} - -AtomMatcher CreateUidProcessStateChangedAtomMatcher() { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId("UidProcessStateChanged")); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED); - return atom_matcher; -} - -AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name, - WakelockStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(4); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateAcquireWakelockAtomMatcher() { - return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE); -} - -AtomMatcher CreateReleaseWakelockAtomMatcher() { - return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE); -} - -AtomMatcher CreateScreenStateChangedAtomMatcher( - const string& name, android::view::DisplayStateEnum state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(1); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateScreenTurnedOnAtomMatcher() { - return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", - android::view::DisplayStateEnum::DISPLAY_STATE_ON); -} - -AtomMatcher CreateScreenTurnedOffAtomMatcher() { - return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff", - ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF); -} - -AtomMatcher CreateSyncStateChangedAtomMatcher( - const string& name, SyncStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(3); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateSyncStartAtomMatcher() { - return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON); -} - -AtomMatcher CreateSyncEndAtomMatcher() { - return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF); -} - -AtomMatcher CreateActivityForegroundStateChangedAtomMatcher( - const string& name, ActivityForegroundStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(4); // Activity field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateMoveToBackgroundAtomMatcher() { - return CreateActivityForegroundStateChangedAtomMatcher( - "MoveToBackground", ActivityForegroundStateChanged::BACKGROUND); -} - -AtomMatcher CreateMoveToForegroundAtomMatcher() { - return CreateActivityForegroundStateChangedAtomMatcher( - "MoveToForeground", ActivityForegroundStateChanged::FOREGROUND); -} - -Predicate CreateScheduledJobPredicate() { - Predicate predicate; - predicate.set_id(StringToId("ScheduledJobRunningPredicate")); - predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish")); - return predicate; -} - -Predicate CreateBatterySaverModePredicate() { - Predicate predicate; - predicate.set_id(StringToId("BatterySaverIsOn")); - predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop")); - return predicate; -} - -Predicate CreateScreenIsOnPredicate() { - Predicate predicate; - predicate.set_id(StringToId("ScreenIsOn")); - predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff")); - return predicate; -} - -Predicate CreateScreenIsOffPredicate() { - Predicate predicate; - predicate.set_id(1111123); - predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn")); - return predicate; -} - -Predicate CreateHoldingWakelockPredicate() { - Predicate predicate; - predicate.set_id(StringToId("HoldingWakelock")); - predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock")); - return predicate; -} - -Predicate CreateIsSyncingPredicate() { - Predicate predicate; - predicate.set_id(33333333333333); - predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd")); - return predicate; -} - -Predicate CreateIsInBackgroundPredicate() { - Predicate predicate; - predicate.set_id(StringToId("IsInBackground")); - predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground")); - predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground")); - return predicate; -} - -void addPredicateToPredicateCombination(const Predicate& predicate, - Predicate* combinationPredicate) { - combinationPredicate->mutable_combination()->add_predicate(predicate.id()); -} - -FieldMatcher CreateAttributionUidDimensions(const int atomId, - const std::vector<Position>& positions) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const auto position : positions) { - auto child = dimensions.add_child(); - child->set_field(1); - child->set_position(position); - child->add_child()->set_field(1); - } - return dimensions; -} - -FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, - const std::vector<Position>& positions) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const auto position : positions) { - auto child = dimensions.add_child(); - child->set_field(1); - child->set_position(position); - child->add_child()->set_field(1); - child->add_child()->set_field(2); - } - return dimensions; -} - -FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const int field : fields) { - dimensions.add_child()->set_field(field); - } - return dimensions; -} - -void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids, - const vector<string>& attributionTags) { - 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()); -} - -void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) { - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - - AStatsEvent_release(statsEvent); -} - -std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( - uint64_t timestampNs, const android::view::DisplayStateEnum state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent( - const vector<int>& attributionUids, const vector<string>& attributionTags, - const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, jobName.c_str()); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& jobName) { - return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, - ScheduledJobStateChanged::STARTED, timestampNs); -} - -// Create log event when scheduled job finishes. -std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& jobName) { - return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, - ScheduledJobStateChanged::FINISHED, timestampNs); -} - -std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& name, - const SyncStateChanged::State state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, name.c_str()); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& name) { - return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, - SyncStateChanged::ON); -} - -std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& name) { - return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, - SyncStateChanged::OFF); -} - -sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, - const ConfigKey& key) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - sp<StatsLogProcessor> processor = - new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; }, - [](const int&, const vector<int64_t>&) { return true; }); - processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config); - return processor; -} - -void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) { - std::sort(events->begin(), events->end(), - [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) { - return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs(); - }); -} - -int64_t StringToId(const string& str) { - return static_cast<int64_t>(std::hash<std::string>()(str)); -} - - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h deleted file mode 100644 index 3efaa850a921..000000000000 --- a/cmds/statsd/benchmark/metric_util.h +++ /dev/null @@ -1,140 +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. - -#pragma once - -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "src/StatsLogProcessor.h" -#include "src/logd/LogEvent.h" -#include "stats_event.h" -#include "statslog.h" - -namespace android { -namespace os { -namespace statsd { - -// Create AtomMatcher proto to simply match a specific atom type. -AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId); - -// Create AtomMatcher proto for scheduled job state changed. -AtomMatcher CreateScheduledJobStateChangedAtomMatcher(); - -// Create AtomMatcher proto for starting a scheduled job. -AtomMatcher CreateStartScheduledJobAtomMatcher(); - -// Create AtomMatcher proto for a scheduled job is done. -AtomMatcher CreateFinishScheduledJobAtomMatcher(); - -// Create AtomMatcher proto for screen brightness state changed. -AtomMatcher CreateScreenBrightnessChangedAtomMatcher(); - -// Create AtomMatcher proto for acquiring wakelock. -AtomMatcher CreateAcquireWakelockAtomMatcher(); - -// Create AtomMatcher proto for releasing wakelock. -AtomMatcher CreateReleaseWakelockAtomMatcher() ; - -// Create AtomMatcher proto for screen turned on. -AtomMatcher CreateScreenTurnedOnAtomMatcher(); - -// Create AtomMatcher proto for screen turned off. -AtomMatcher CreateScreenTurnedOffAtomMatcher(); - -// Create AtomMatcher proto for app sync turned on. -AtomMatcher CreateSyncStartAtomMatcher(); - -// Create AtomMatcher proto for app sync turned off. -AtomMatcher CreateSyncEndAtomMatcher(); - -// Create AtomMatcher proto for app sync moves to background. -AtomMatcher CreateMoveToBackgroundAtomMatcher(); - -// Create AtomMatcher proto for app sync moves to foreground. -AtomMatcher CreateMoveToForegroundAtomMatcher(); - -// Create Predicate proto for screen is off. -Predicate CreateScreenIsOffPredicate(); - -// Create Predicate proto for a running scheduled job. -Predicate CreateScheduledJobPredicate(); - -// Create Predicate proto for holding wakelock. -Predicate CreateHoldingWakelockPredicate(); - -// Create a Predicate proto for app syncing. -Predicate CreateIsSyncingPredicate(); - -// Create a Predicate proto for app is in background. -Predicate CreateIsInBackgroundPredicate(); - -// Add a predicate to the predicate combination. -void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination); - -// Create dimensions from primitive fields. -FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields); - -// Create dimensions by attribution uid and tag. -FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, - const std::vector<Position>& positions); - -// Create dimensions by attribution uid only. -FieldMatcher CreateAttributionUidDimensions(const int atomId, - const std::vector<Position>& positions); - -void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids, - const vector<string>& attributionTags); - -void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent); - -// Create log event for screen state changed. -std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( - uint64_t timestampNs, const android::view::DisplayStateEnum state); - -// Create log event when scheduled job starts. -std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& jobName); - -// Create log event when scheduled job finishes. -std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& jobName); - -// Create log event when the app sync starts. -std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& name); - -// Create log event when the app sync ends. -std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& name); - -// Create a statsd log event processor upon the start time in seconds, config and key. -sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, - const ConfigKey& key); - -// Util function to sort the log events by timestamp. -void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events); - -int64_t StringToId(const string& str); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/stats_write_benchmark.cpp b/cmds/statsd/benchmark/stats_write_benchmark.cpp deleted file mode 100644 index f5a0cd5dfb39..000000000000 --- a/cmds/statsd/benchmark/stats_write_benchmark.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ -#include "benchmark/benchmark.h" -#include <statslog.h> - -namespace android { -namespace os { -namespace statsd { - -static void BM_StatsWrite(benchmark::State& state) { - const char* reason = "test"; - int64_t boot_end_time = 1234567; - int64_t total_duration = 100; - int64_t bootloader_duration = 10; - int64_t time_since_last_boot = 99999999; - while (state.KeepRunning()) { - android::util::stats_write( - android::util::BOOT_SEQUENCE_REPORTED, reason, reason, - boot_end_time, total_duration, bootloader_duration, time_since_last_boot); - total_duration++; - } -} -BENCHMARK(BM_StatsWrite); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp deleted file mode 100644 index c9ccfb93c86d..000000000000 --- a/cmds/statsd/src/FieldValue.cpp +++ /dev/null @@ -1,474 +0,0 @@ -/* - * 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. - */ - -#define DEBUG false -#include "Log.h" -#include "FieldValue.h" -#include "HashableDimensionKey.h" -#include "math.h" - -namespace android { -namespace os { -namespace statsd { - -int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth) { - int32_t field = 0; - for (int32_t i = 0; i <= depth; i++) { - int32_t shiftBits = 8 * (kMaxLogDepth - i); - field |= (pos[i] << shiftBits); - } - - if (includeDepth) { - field |= (depth << 24); - } - return field; -} - -int32_t encodeMatcherMask(int32_t mask[], int32_t depth) { - return getEncodedField(mask, depth, false) | 0xff000000; -} - -bool Field::matches(const Matcher& matcher) const { - if (mTag != matcher.mMatcher.getTag()) { - return false; - } - if ((mField & matcher.mMask) == matcher.mMatcher.getField()) { - return true; - } - - if (matcher.hasAllPositionMatcher() && - (mField & (matcher.mMask & kClearAllPositionMatcherMask)) == matcher.mMatcher.getField()) { - return true; - } - - return false; -} - -void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* pos, int* mask, - std::vector<Matcher>* output) { - if (depth > kMaxLogDepth) { - ALOGE("depth > 2"); - return; - } - - pos[depth] = matcher.field(); - mask[depth] = 0x7f; - - if (matcher.has_position()) { - depth++; - if (depth > 2) { - return; - } - switch (matcher.position()) { - case Position::ALL: - pos[depth] = 0x00; - mask[depth] = 0x7f; - break; - case Position::ANY: - pos[depth] = 0; - mask[depth] = 0; - break; - case Position::FIRST: - pos[depth] = 1; - mask[depth] = 0x7f; - break; - case Position::LAST: - pos[depth] = 0x80; - mask[depth] = 0x80; - break; - case Position::POSITION_UNKNOWN: - pos[depth] = 0; - mask[depth] = 0; - break; - } - } - - if (matcher.child_size() == 0) { - output->push_back(Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth))); - } else { - for (const auto& child : matcher.child()) { - translateFieldMatcher(tag, child, depth + 1, pos, mask, output); - } - } -} - -void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output) { - int pos[] = {1, 1, 1}; - int mask[] = {0x7f, 0x7f, 0x7f}; - int tag = matcher.field(); - for (const auto& child : matcher.child()) { - translateFieldMatcher(tag, child, 0, pos, mask, output); - } -} - -bool isAttributionUidField(const FieldValue& value) { - return isAttributionUidField(value.mField, value.mValue); -} - -int32_t getUidIfExists(const FieldValue& value) { - // the field is uid field if the field is the uid field in attribution node - // or annotated as such in the atom - bool isUid = isAttributionUidField(value) || isUidField(value); - return isUid ? value.mValue.int_value : -1; -} - -bool isAttributionUidField(const Field& field, const Value& value) { - int f = field.getField() & 0xff007f; - if (f == 0x10001 && value.getType() == INT) { - return true; - } - return false; -} - -bool isUidField(const FieldValue& fieldValue) { - return fieldValue.mAnnotations.isUidField(); -} - -Value::Value(const Value& from) { - type = from.getType(); - switch (type) { - case INT: - int_value = from.int_value; - break; - case LONG: - long_value = from.long_value; - break; - case FLOAT: - float_value = from.float_value; - break; - case DOUBLE: - double_value = from.double_value; - break; - case STRING: - str_value = from.str_value; - break; - case STORAGE: - storage_value = from.storage_value; - break; - default: - break; - } -} - -std::string Value::toString() const { - switch (type) { - case INT: - return std::to_string(int_value) + "[I]"; - case LONG: - return std::to_string(long_value) + "[L]"; - case FLOAT: - return std::to_string(float_value) + "[F]"; - case DOUBLE: - return std::to_string(double_value) + "[D]"; - case STRING: - return str_value + "[S]"; - case STORAGE: - return "bytes of size " + std::to_string(storage_value.size()) + "[ST]"; - default: - return "[UNKNOWN]"; - } -} - -bool Value::isZero() const { - switch (type) { - case INT: - return int_value == 0; - case LONG: - return long_value == 0; - case FLOAT: - return fabs(float_value) <= std::numeric_limits<float>::epsilon(); - case DOUBLE: - return fabs(double_value) <= std::numeric_limits<double>::epsilon(); - case STRING: - return str_value.size() == 0; - case STORAGE: - return storage_value.size() == 0; - default: - return false; - } -} - -bool Value::operator==(const Value& that) const { - if (type != that.getType()) return false; - - switch (type) { - case INT: - return int_value == that.int_value; - case LONG: - return long_value == that.long_value; - case FLOAT: - return float_value == that.float_value; - case DOUBLE: - return double_value == that.double_value; - case STRING: - return str_value == that.str_value; - case STORAGE: - return storage_value == that.storage_value; - default: - return false; - } -} - -bool Value::operator!=(const Value& that) const { - if (type != that.getType()) return true; - switch (type) { - case INT: - return int_value != that.int_value; - case LONG: - return long_value != that.long_value; - case FLOAT: - return float_value != that.float_value; - case DOUBLE: - return double_value != that.double_value; - case STRING: - return str_value != that.str_value; - case STORAGE: - return storage_value != that.storage_value; - default: - return false; - } -} - -bool Value::operator<(const Value& that) const { - if (type != that.getType()) return type < that.getType(); - - switch (type) { - case INT: - return int_value < that.int_value; - case LONG: - return long_value < that.long_value; - case FLOAT: - return float_value < that.float_value; - case DOUBLE: - return double_value < that.double_value; - case STRING: - return str_value < that.str_value; - case STORAGE: - return storage_value < that.storage_value; - default: - return false; - } -} - -bool Value::operator>(const Value& that) const { - if (type != that.getType()) return type > that.getType(); - - switch (type) { - case INT: - return int_value > that.int_value; - case LONG: - return long_value > that.long_value; - case FLOAT: - return float_value > that.float_value; - case DOUBLE: - return double_value > that.double_value; - case STRING: - return str_value > that.str_value; - case STORAGE: - return storage_value > that.storage_value; - default: - return false; - } -} - -bool Value::operator>=(const Value& that) const { - if (type != that.getType()) return type >= that.getType(); - - switch (type) { - case INT: - return int_value >= that.int_value; - case LONG: - return long_value >= that.long_value; - case FLOAT: - return float_value >= that.float_value; - case DOUBLE: - return double_value >= that.double_value; - case STRING: - return str_value >= that.str_value; - case STORAGE: - return storage_value >= that.storage_value; - default: - return false; - } -} - -Value Value::operator-(const Value& that) const { - Value v; - if (type != that.type) { - ALOGE("Can't operate on different value types, %d, %d", type, that.type); - return v; - } - if (type == STRING) { - ALOGE("Can't operate on string value type"); - return v; - } - - if (type == STORAGE) { - ALOGE("Can't operate on storage value type"); - return v; - } - - switch (type) { - case INT: - v.setInt(int_value - that.int_value); - break; - case LONG: - v.setLong(long_value - that.long_value); - break; - case FLOAT: - v.setFloat(float_value - that.float_value); - break; - case DOUBLE: - v.setDouble(double_value - that.double_value); - break; - default: - break; - } - return v; -} - -Value& Value::operator=(const Value& that) { - type = that.type; - switch (type) { - case INT: - int_value = that.int_value; - break; - case LONG: - long_value = that.long_value; - break; - case FLOAT: - float_value = that.float_value; - break; - case DOUBLE: - double_value = that.double_value; - break; - case STRING: - str_value = that.str_value; - break; - case STORAGE: - storage_value = that.storage_value; - break; - default: - break; - } - return *this; -} - -Value& Value::operator+=(const Value& that) { - if (type != that.type) { - ALOGE("Can't operate on different value types, %d, %d", type, that.type); - return *this; - } - if (type == STRING) { - ALOGE("Can't operate on string value type"); - return *this; - } - if (type == STORAGE) { - ALOGE("Can't operate on storage value type"); - return *this; - } - - switch (type) { - case INT: - int_value += that.int_value; - break; - case LONG: - long_value += that.long_value; - break; - case FLOAT: - float_value += that.float_value; - break; - case DOUBLE: - double_value += that.double_value; - break; - default: - break; - } - return *this; -} - -double Value::getDouble() const { - switch (type) { - case INT: - return int_value; - case LONG: - return long_value; - case FLOAT: - return float_value; - case DOUBLE: - return double_value; - default: - return 0; - } -} - -bool equalDimensions(const std::vector<Matcher>& dimension_a, - const std::vector<Matcher>& dimension_b) { - bool eq = dimension_a.size() == dimension_b.size(); - for (size_t i = 0; eq && i < dimension_a.size(); ++i) { - if (dimension_b[i] != dimension_a[i]) { - eq = false; - } - } - return eq; -} - -bool subsetDimensions(const std::vector<Matcher>& dimension_a, - const std::vector<Matcher>& dimension_b) { - if (dimension_a.size() > dimension_b.size()) { - return false; - } - for (size_t i = 0; i < dimension_a.size(); ++i) { - bool found = false; - for (size_t j = 0; j < dimension_b.size(); ++j) { - if (dimension_a[i] == dimension_b[j]) { - found = true; - } - } - if (!found) { - return false; - } - } - return true; -} - -bool HasPositionANY(const FieldMatcher& matcher) { - if (matcher.has_position() && matcher.position() == Position::ANY) { - return true; - } - for (const auto& child : matcher.child()) { - if (HasPositionANY(child)) { - return true; - } - } - return false; -} - -bool HasPositionALL(const FieldMatcher& matcher) { - if (matcher.has_position() && matcher.position() == Position::ALL) { - return true; - } - for (const auto& child : matcher.child()) { - if (HasPositionALL(child)) { - return true; - } - } - return false; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h deleted file mode 100644 index fd86e3683970..000000000000 --- a/cmds/statsd/src/FieldValue.h +++ /dev/null @@ -1,462 +0,0 @@ -/* - * 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. - */ -#pragma once - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "annotations.h" - -namespace android { -namespace os { -namespace statsd { - -class HashableDimensionKey; -struct Matcher; -struct Field; -struct FieldValue; - -const int32_t kMaxLogDepth = 2; -const int32_t kLastBitMask = 0x80; -const int32_t kClearLastBitDeco = 0x7f; -const int32_t kClearAllPositionMatcherMask = 0xffff00ff; - -enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING, STORAGE }; - -int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth); - -int32_t encodeMatcherMask(int32_t mask[], int32_t depth); - -// Get the encoded field for a leaf with a [field] number at depth 0; -inline int32_t getSimpleField(size_t field) { - return ((int32_t)field << 8 * 2); -} - -/** - * Field is a wrapper class for 2 integers that represents the field of a log element in its Atom - * proto. - * [mTag]: the atom id. - * [mField]: encoded path from the root (atom) to leaf. - * - * For example: - * WakeLockStateChanged { - * repeated AttributionNode = 1; - * int state = 2; - * string tag = 3; - * } - * Read from logd, the items are structured as below: - * [[[1000, "tag"], [2000, "tag2"],], 2,"hello"] - * - * When we read through the list, we will encode each field in a 32bit integer. - * 8bit segments |--------|--------|--------|--------| - * Depth field0 [L]field1 [L]field1 - * - * The first 8 bits are the depth of the field. for example, the uid 1000 has depth 2. - * The following 3 8-bit are for the item's position at each level. - * The first bit of each 8bits field is reserved to mark if the item is the last item at that level - * this is to make matching easier later. - * - * The above wakelock event is translated into FieldValue pairs. - * 0x02010101->1000 - * 0x02010182->tag - * 0x02018201->2000 - * 0x02018282->tag2 - * 0x00020000->2 - * 0x00030000->"hello" - * - * This encoding is the building block for the later operations. - * Please see the definition for Matcher below to see how the matching is done. - */ -struct Field { -private: - int32_t mTag; - int32_t mField; - -public: - Field() {} - - Field(int32_t tag, int32_t pos[], int32_t depth) : mTag(tag) { - mField = getEncodedField(pos, depth, true); - } - - Field(const Field& from) : mTag(from.getTag()), mField(from.getField()) { - } - - Field(int32_t tag, int32_t field) : mTag(tag), mField(field){}; - - inline void setField(int32_t field) { - mField = field; - } - - inline void setTag(int32_t tag) { - mTag = tag; - } - - inline void decorateLastPos(int32_t depth) { - int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth); - mField |= mask; - } - - inline int32_t getTag() const { - return mTag; - } - - inline int32_t getDepth() const { - return (mField >> 24); - } - - inline int32_t getPath(int32_t depth) const { - if (depth > 2 || depth < 0) return 0; - - int32_t field = (mField & 0x00ffffff); - int32_t mask = 0xffffffff; - return (field & (mask << 8 * (kMaxLogDepth - depth))); - } - - inline int32_t getPrefix(int32_t depth) const { - if (depth == 0) return 0; - return getPath(depth - 1); - } - - inline int32_t getField() const { - return mField; - } - - inline int32_t getRawPosAtDepth(int32_t depth) const { - int32_t field = (mField & 0x00ffffff); - int32_t shift = 8 * (kMaxLogDepth - depth); - int32_t mask = 0xff << shift; - - return (field & mask) >> shift; - } - - inline int32_t getPosAtDepth(int32_t depth) const { - return getRawPosAtDepth(depth) & kClearLastBitDeco; - } - - // Check if the first bit of the 8-bit segment for depth is 1 - inline bool isLastPos(int32_t depth) const { - int32_t field = (mField & 0x00ffffff); - int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth); - return (field & mask) != 0; - } - - // if the 8-bit segment is all 0's - inline bool isAnyPosMatcher(int32_t depth) const { - return getDepth() >= depth && getRawPosAtDepth(depth) == 0; - } - // if the 8bit is 0x80 (1000 0000) - inline bool isLastPosMatcher(int32_t depth) const { - return getDepth() >= depth && getRawPosAtDepth(depth) == kLastBitMask; - } - - inline bool operator==(const Field& that) const { - return mTag == that.getTag() && mField == that.getField(); - }; - - inline bool operator!=(const Field& that) const { - return mTag != that.getTag() || mField != that.getField(); - }; - - bool operator<(const Field& that) const { - if (mTag != that.getTag()) { - return mTag < that.getTag(); - } - - if (mField != that.getField()) { - return mField < that.getField(); - } - - return false; - } - - bool matches(const Matcher& that) const; -}; - -/** - * Matcher represents a leaf matcher in the FieldMatcher in statsd_config. - * - * It contains all information needed to match one or more leaf node. - * All information is encoded in a Field(2 ints) and a bit mask(1 int). - * - * For example, to match the first/any/last uid field in attribution chain in Atom 10, - * we have the following FieldMatcher in statsd_config - * FieldMatcher { - * field:10 - * FieldMatcher { - * field:1 - * position: any/last/first - * FieldMatcher { - * field:1 - * } - * } - * } - * - * We translate the FieldMatcher into a Field, and mask - * First: [Matcher Field] 0x02010101 [Mask]0xff7f7f7f - * Last: [Matcher Field] 0x02018001 [Mask]0xff7f807f - * Any: [Matcher Field] 0x02010001 [Mask]0xff7f007f - * All: [Matcher Field] 0x02010001 [Mask]0xff7f7f7f - * - * [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if - * the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are - * equal. Nothing can beat the performance of this matching algorithm. - * - * TODO(b/110561213): ADD EXAMPLE HERE. - */ -struct Matcher { - Matcher(const Field& matcher, int32_t mask) : mMatcher(matcher), mMask(mask){}; - - const Field mMatcher; - const int32_t mMask; - - inline const Field& getMatcher() const { - return mMatcher; - } - - inline int32_t getMask() const { - return mMask; - } - - inline int32_t getRawMaskAtDepth(int32_t depth) const { - int32_t field = (mMask & 0x00ffffff); - int32_t shift = 8 * (kMaxLogDepth - depth); - int32_t mask = 0xff << shift; - - return (field & mask) >> shift; - } - - bool hasAllPositionMatcher() const { - return mMatcher.getDepth() == 2 && getRawMaskAtDepth(1) == 0x7f; - } - - bool hasAnyPositionMatcher(int* prefix) const { - if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(1) == 0) { - (*prefix) = mMatcher.getPrefix(1); - return true; - } - return false; - } - - inline bool operator!=(const Matcher& that) const { - return mMatcher != that.getMatcher() || mMask != that.getMask(); - } - - inline bool operator==(const Matcher& that) const { - return mMatcher == that.mMatcher && mMask == that.mMask; - } -}; - -inline Matcher getSimpleMatcher(int32_t tag, size_t field) { - return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000); -} - -inline Matcher getFirstUidMatcher(int32_t atomId) { - int32_t pos[] = {1, 1, 1}; - return Matcher(Field(atomId, pos, 2), 0xff7f7f7f); -} - -/** - * A wrapper for a union type to contain multiple types of values. - * - */ -struct Value { - Value() : type(UNKNOWN) {} - - Value(int32_t v) { - int_value = v; - type = INT; - } - - Value(int64_t v) { - long_value = v; - type = LONG; - } - - Value(float v) { - float_value = v; - type = FLOAT; - } - - Value(double v) { - double_value = v; - type = DOUBLE; - } - - Value(const std::string& v) { - str_value = v; - type = STRING; - } - - Value(const std::vector<uint8_t>& v) { - storage_value = v; - type = STORAGE; - } - - void setInt(int32_t v) { - int_value = v; - type = INT; - } - - void setLong(int64_t v) { - long_value = v; - type = LONG; - } - - void setFloat(float v) { - float_value = v; - type = FLOAT; - } - - void setDouble(double v) { - double_value = v; - type = DOUBLE; - } - - union { - int32_t int_value; - int64_t long_value; - float float_value; - double double_value; - }; - std::string str_value; - std::vector<uint8_t> storage_value; - - Type type; - - std::string toString() const; - - bool isZero() const; - - Type getType() const { - return type; - } - - double getDouble() const; - - Value(const Value& from); - - bool operator==(const Value& that) const; - bool operator!=(const Value& that) const; - - bool operator<(const Value& that) const; - bool operator>(const Value& that) const; - bool operator>=(const Value& that) const; - Value operator-(const Value& that) const; - Value& operator+=(const Value& that); - Value& operator=(const Value& that); -}; - -class Annotations { -public: - Annotations() { - setNested(true); // Nested = true by default - } - - // This enum stores where particular annotations can be found in the - // bitmask. Note that these pos do not correspond to annotation ids. - enum { - NESTED_POS = 0x0, - PRIMARY_POS = 0x1, - EXCLUSIVE_POS = 0x2, - UID_POS = 0x3 - }; - - inline void setNested(bool nested) { setBitmaskAtPos(NESTED_POS, nested); } - - inline void setPrimaryField(bool primary) { setBitmaskAtPos(PRIMARY_POS, primary); } - - inline void setExclusiveState(bool exclusive) { setBitmaskAtPos(EXCLUSIVE_POS, exclusive); } - - inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); } - - // Default value = false - inline bool isNested() const { return getValueFromBitmask(NESTED_POS); } - - // Default value = false - inline bool isPrimaryField() const { return getValueFromBitmask(PRIMARY_POS); } - - // Default value = false - inline bool isExclusiveState() const { return getValueFromBitmask(EXCLUSIVE_POS); } - - // Default value = false - inline bool isUidField() const { return getValueFromBitmask(UID_POS); } - -private: - inline void setBitmaskAtPos(int pos, bool value) { - mBooleanBitmask &= ~(1 << pos); // clear - mBooleanBitmask |= (value << pos); // set - } - - inline bool getValueFromBitmask(int pos) const { - return (mBooleanBitmask >> pos) & 0x1; - } - - // This is a bitmask over all annotations stored in boolean form. Because - // there are only 4 booleans, just one byte is required. - uint8_t mBooleanBitmask = 0; -}; - -/** - * Represents a log item, or a dimension item (They are essentially the same). - */ -struct FieldValue { - FieldValue() {} - FieldValue(const Field& field, const Value& value) : mField(field), mValue(value) { - } - bool operator==(const FieldValue& that) const { - return mField == that.mField && mValue == that.mValue; - } - bool operator!=(const FieldValue& that) const { - return mField != that.mField || mValue != that.mValue; - } - bool operator<(const FieldValue& that) const { - if (mField != that.mField) { - return mField < that.mField; - } - - if (mValue != that.mValue) { - return mValue < that.mValue; - } - - return false; - } - - Field mField; - Value mValue; - Annotations mAnnotations; -}; - -bool HasPositionANY(const FieldMatcher& matcher); -bool HasPositionALL(const FieldMatcher& matcher); - -bool isAttributionUidField(const FieldValue& value); - -/* returns uid if the field is uid field, or -1 if the field is not a uid field */ -int getUidIfExists(const FieldValue& value); - -void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output); - -bool isAttributionUidField(const Field& field, const Value& value); -bool isUidField(const FieldValue& fieldValue); - -bool equalDimensions(const std::vector<Matcher>& dimension_a, - const std::vector<Matcher>& dimension_b); - -// Returns true if dimension_a is a subset of dimension_b. -bool subsetDimensions(const std::vector<Matcher>& dimension_a, - const std::vector<Matcher>& dimension_b); -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp deleted file mode 100644 index eba66e0cb7b0..000000000000 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ /dev/null @@ -1,381 +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. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "HashableDimensionKey.h" -#include "FieldValue.h" - -namespace android { -namespace os { -namespace statsd { - -using std::string; -using std::vector; -using android::base::StringPrintf; - -// These constants must be kept in sync with those in StatsDimensionsValue.java -const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2; -const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3; -const static int STATS_DIMENSIONS_VALUE_LONG_TYPE = 4; -// const static int STATS_DIMENSIONS_VALUE_BOOL_TYPE = 5; (commented out because -// unused -- statsd does not correctly support bool types) -const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6; -const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7; - -/** - * Recursive helper function that populates a parent StatsDimensionsValueParcel - * with children StatsDimensionsValueParcels. - * - * \param parent parcel that will be populated with children - * \param childDepth depth of children FieldValues - * \param childPrefix expected FieldValue prefix of children - * \param dims vector of FieldValues stored by HashableDimensionKey - * \param index position in dims to start reading children from - */ -static void populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel& parent, - int childDepth, int childPrefix, - const vector<FieldValue>& dims, - size_t& index) { - if (childDepth > 2) { - ALOGE("Depth > 2 not supported by StatsDimensionsValueParcel."); - return; - } - - while (index < dims.size()) { - const FieldValue& dim = dims[index]; - int fieldDepth = dim.mField.getDepth(); - int fieldPrefix = dim.mField.getPrefix(childDepth); - - StatsDimensionsValueParcel child; - child.field = dim.mField.getPosAtDepth(childDepth); - - if (fieldDepth == childDepth && fieldPrefix == childPrefix) { - switch (dim.mValue.getType()) { - case INT: - child.valueType = STATS_DIMENSIONS_VALUE_INT_TYPE; - child.intValue = dim.mValue.int_value; - break; - case LONG: - child.valueType = STATS_DIMENSIONS_VALUE_LONG_TYPE; - child.longValue = dim.mValue.long_value; - break; - case FLOAT: - child.valueType = STATS_DIMENSIONS_VALUE_FLOAT_TYPE; - child.floatValue = dim.mValue.float_value; - break; - case STRING: - child.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE; - child.stringValue = dim.mValue.str_value; - break; - default: - ALOGE("Encountered FieldValue with unsupported value type."); - break; - } - index++; - parent.tupleValue.push_back(child); - } else if (fieldDepth > childDepth && fieldPrefix == childPrefix) { - // This FieldValue is not a child of the current parent, but it is - // an indirect descendant. Thus, create a direct child of TUPLE_TYPE - // and recurse to parcel the indirect descendants. - child.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE; - populateStatsDimensionsValueParcelChildren(child, childDepth + 1, - dim.mField.getPrefix(childDepth + 1), dims, - index); - parent.tupleValue.push_back(child); - } else { - return; - } - } -} - -StatsDimensionsValueParcel HashableDimensionKey::toStatsDimensionsValueParcel() const { - StatsDimensionsValueParcel root; - if (mValues.size() == 0) { - return root; - } - - root.field = mValues[0].mField.getTag(); - root.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE; - - // Children of the root correspond to top-level (depth = 0) FieldValues. - int childDepth = 0; - int childPrefix = 0; - size_t index = 0; - populateStatsDimensionsValueParcelChildren(root, childDepth, childPrefix, mValues, index); - - return root; -} - -android::hash_t hashDimension(const HashableDimensionKey& value) { - android::hash_t hash = 0; - for (const auto& fieldValue : value.getValues()) { - hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getField())); - hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getTag())); - hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mValue.getType())); - switch (fieldValue.mValue.getType()) { - case INT: - hash = android::JenkinsHashMix(hash, - android::hash_type(fieldValue.mValue.int_value)); - break; - case LONG: - hash = android::JenkinsHashMix(hash, - android::hash_type(fieldValue.mValue.long_value)); - break; - case STRING: - hash = android::JenkinsHashMix(hash, static_cast<uint32_t>(std::hash<std::string>()( - fieldValue.mValue.str_value))); - break; - case FLOAT: { - hash = android::JenkinsHashMix(hash, - android::hash_type(fieldValue.mValue.float_value)); - break; - } - default: - break; - } - } - return JenkinsHashWhiten(hash); -} - -bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values, - FieldValue* output) { - for (const auto& value : values) { - if (value.mField.matches(matcherField)) { - (*output) = value; - return true; - } - } - return false; -} - -bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values, - HashableDimensionKey* output) { - size_t num_matches = 0; - for (const auto& value : values) { - for (size_t i = 0; i < matcherFields.size(); ++i) { - const auto& matcher = matcherFields[i]; - if (value.mField.matches(matcher)) { - output->addValue(value); - output->mutableValue(num_matches)->mField.setTag(value.mField.getTag()); - output->mutableValue(num_matches)->mField.setField( - value.mField.getField() & matcher.mMask); - num_matches++; - } - } - } - return num_matches > 0; -} - -bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output) { - size_t num_matches = 0; - const int32_t simpleFieldMask = 0xff7f0000; - const int32_t attributionUidFieldMask = 0xff7f7f7f; - for (const auto& value : values) { - if (value.mAnnotations.isPrimaryField()) { - output->addValue(value); - output->mutableValue(num_matches)->mField.setTag(value.mField.getTag()); - const int32_t mask = - isAttributionUidField(value) ? attributionUidFieldMask : simpleFieldMask; - output->mutableValue(num_matches)->mField.setField(value.mField.getField() & mask); - num_matches++; - } - } - return num_matches > 0; -} - -void filterGaugeValues(const std::vector<Matcher>& matcherFields, - const std::vector<FieldValue>& values, std::vector<FieldValue>* output) { - for (const auto& field : matcherFields) { - for (const auto& value : values) { - if (value.mField.matches(field)) { - output->push_back(value); - } - } - } -} - -void getDimensionForCondition(const std::vector<FieldValue>& eventValues, - const Metric2Condition& links, - HashableDimensionKey* conditionDimension) { - // Get the dimension first by using dimension from what. - filterValues(links.metricFields, eventValues, conditionDimension); - - size_t count = conditionDimension->getValues().size(); - if (count != links.conditionFields.size()) { - return; - } - - for (size_t i = 0; i < count; i++) { - conditionDimension->mutableValue(i)->mField.setField( - links.conditionFields[i].mMatcher.getField()); - conditionDimension->mutableValue(i)->mField.setTag( - links.conditionFields[i].mMatcher.getTag()); - } -} - -void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link, - HashableDimensionKey* statePrimaryKey) { - // First, get the dimension from the event using the "what" fields from the - // MetricStateLinks. - filterValues(link.metricFields, eventValues, statePrimaryKey); - - // Then check that the statePrimaryKey size equals the number of state fields - size_t count = statePrimaryKey->getValues().size(); - if (count != link.stateFields.size()) { - return; - } - - // For each dimension Value in the statePrimaryKey, set the field and tag - // using the state atom fields from MetricStateLinks. - for (size_t i = 0; i < count; i++) { - statePrimaryKey->mutableValue(i)->mField.setField(link.stateFields[i].mMatcher.getField()); - statePrimaryKey->mutableValue(i)->mField.setTag(link.stateFields[i].mMatcher.getTag()); - } -} - -bool containsLinkedStateValues(const HashableDimensionKey& whatKey, - const HashableDimensionKey& primaryKey, - const vector<Metric2State>& stateLinks, const int32_t stateAtomId) { - if (whatKey.getValues().size() < primaryKey.getValues().size()) { - ALOGE("Contains linked values false: whatKey is too small"); - return false; - } - - for (const auto& primaryValue : primaryKey.getValues()) { - bool found = false; - for (const auto& whatValue : whatKey.getValues()) { - if (linked(stateLinks, stateAtomId, primaryValue.mField, whatValue.mField) && - primaryValue.mValue == whatValue.mValue) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - return true; -} - -bool linked(const vector<Metric2State>& stateLinks, const int32_t stateAtomId, - const Field& stateField, const Field& metricField) { - for (auto stateLink : stateLinks) { - if (stateLink.stateAtomId != stateAtomId) { - continue; - } - - for (size_t i = 0; i < stateLink.stateFields.size(); i++) { - if (stateLink.stateFields[i].mMatcher == stateField && - stateLink.metricFields[i].mMatcher == metricField) { - return true; - } - } - } - return false; -} - -bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) { - if (s1.size() != s2.size()) { - return s1.size() < s2.size(); - } - - size_t count = s1.size(); - for (size_t i = 0; i < count; i++) { - if (s1[i] != s2[i]) { - return s1[i] < s2[i]; - } - } - return false; -} - -bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const { - return !((*this) == that); -} - -bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const { - if (mValues.size() != that.getValues().size()) { - return false; - } - size_t count = mValues.size(); - for (size_t i = 0; i < count; i++) { - if (mValues[i] != (that.getValues())[i]) { - return false; - } - } - return true; -}; - -bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const { - return LessThan(getValues(), that.getValues()); -}; - -bool HashableDimensionKey::contains(const HashableDimensionKey& that) const { - if (mValues.size() < that.getValues().size()) { - return false; - } - - if (mValues.size() == that.getValues().size()) { - return (*this) == that; - } - - for (const auto& value : that.getValues()) { - bool found = false; - for (const auto& myValue : mValues) { - if (value.mField == myValue.mField && value.mValue == myValue.mValue) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - - return true; -} - -string HashableDimensionKey::toString() const { - std::string output; - for (const auto& value : mValues) { - output += StringPrintf("(%d)%#x->%s ", value.mField.getTag(), value.mField.getField(), - value.mValue.toString().c_str()); - } - return output; -} - -bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const { - return mDimensionKeyInWhat == that.getDimensionKeyInWhat() && - mStateValuesKey == that.getStateValuesKey(); -}; - -string MetricDimensionKey::toString() const { - return mDimensionKeyInWhat.toString() + mStateValuesKey.toString(); -} - -bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const { - if (mDimensionKeyInWhat < that.getDimensionKeyInWhat()) { - return true; - } else if (that.getDimensionKeyInWhat() < mDimensionKeyInWhat) { - return false; - } - - return mStateValuesKey < that.getStateValuesKey(); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h deleted file mode 100644 index bd011005a301..000000000000 --- a/cmds/statsd/src/HashableDimensionKey.h +++ /dev/null @@ -1,238 +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. - */ - -#pragma once - -#include <aidl/android/os/StatsDimensionsValueParcel.h> -#include <utils/JenkinsHash.h> -#include <vector> -#include "android-base/stringprintf.h" -#include "FieldValue.h" -#include "logd/LogEvent.h" - -namespace android { -namespace os { -namespace statsd { - -using ::aidl::android::os::StatsDimensionsValueParcel; - -struct Metric2Condition { - int64_t conditionId; - std::vector<Matcher> metricFields; - std::vector<Matcher> conditionFields; -}; - -struct Metric2State { - int32_t stateAtomId; - std::vector<Matcher> metricFields; - std::vector<Matcher> stateFields; -}; - -class HashableDimensionKey { -public: - explicit HashableDimensionKey(const std::vector<FieldValue>& values) { - mValues = values; - } - - HashableDimensionKey() {}; - - HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){}; - - inline void addValue(const FieldValue& value) { - mValues.push_back(value); - } - - inline const std::vector<FieldValue>& getValues() const { - return mValues; - } - - inline std::vector<FieldValue>* mutableValues() { - return &mValues; - } - - inline FieldValue* mutableValue(size_t i) { - if (i >= 0 && i < mValues.size()) { - return &(mValues[i]); - } - return nullptr; - } - - StatsDimensionsValueParcel toStatsDimensionsValueParcel() const; - - std::string toString() const; - - bool operator!=(const HashableDimensionKey& that) const; - - bool operator==(const HashableDimensionKey& that) const; - - bool operator<(const HashableDimensionKey& that) const; - - bool contains(const HashableDimensionKey& that) const; - -private: - std::vector<FieldValue> mValues; -}; - -class MetricDimensionKey { -public: - explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat, - const HashableDimensionKey& stateValuesKey) - : mDimensionKeyInWhat(dimensionKeyInWhat), mStateValuesKey(stateValuesKey){}; - - MetricDimensionKey(){}; - - MetricDimensionKey(const MetricDimensionKey& that) - : mDimensionKeyInWhat(that.getDimensionKeyInWhat()), - mStateValuesKey(that.getStateValuesKey()){}; - - MetricDimensionKey& operator=(const MetricDimensionKey& from) = default; - - std::string toString() const; - - inline const HashableDimensionKey& getDimensionKeyInWhat() const { - return mDimensionKeyInWhat; - } - - inline const HashableDimensionKey& getStateValuesKey() const { - return mStateValuesKey; - } - - inline HashableDimensionKey* getMutableStateValuesKey() { - return &mStateValuesKey; - } - - inline void setStateValuesKey(const HashableDimensionKey& key) { - mStateValuesKey = key; - } - - bool hasStateValuesKey() const { - return mStateValuesKey.getValues().size() > 0; - } - - bool operator==(const MetricDimensionKey& that) const; - - bool operator<(const MetricDimensionKey& that) const; - -private: - HashableDimensionKey mDimensionKeyInWhat; - HashableDimensionKey mStateValuesKey; -}; - -android::hash_t hashDimension(const HashableDimensionKey& key); - -/** - * Returns true if a FieldValue field matches the matcher field. - * The value of the FieldValue is output. - */ -bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& values, - FieldValue* output); - -/** - * Creating HashableDimensionKeys from FieldValues using matcher. - * - * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL - * in it. This is because: for example, when we create dimension from last uid in attribution chain, - * In one event, uid 1000 is at position 5 and it's the last - * In another event, uid 1000 is at position 6, and it's the last - * these 2 events should be mapped to the same dimension. So we will remove the original position - * from the dimension key for the uid field (by applying 0x80 bit mask). - */ -bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& values, - HashableDimensionKey* output); - -/** - * Creating HashableDimensionKeys from State Primary Keys in FieldValues. - * - * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL - * in it. This is because: for example, when we create dimension from last uid in attribution chain, - * In one event, uid 1000 is at position 5 and it's the last - * In another event, uid 1000 is at position 6, and it's the last - * these 2 events should be mapped to the same dimension. So we will remove the original position - * from the dimension key for the uid field (by applying 0x80 bit mask). - */ -bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output); - -/** - * Filter the values from FieldValues using the matchers. - * - * In contrast to the above function, this function will not do any modification to the original - * data. Considering it as taking a snapshot on the atom event. - */ -void filterGaugeValues(const std::vector<Matcher>& matchers, const std::vector<FieldValue>& values, - std::vector<FieldValue>* output); - -void getDimensionForCondition(const std::vector<FieldValue>& eventValues, - const Metric2Condition& links, - HashableDimensionKey* conditionDimension); - -/** - * Get dimension values using metric's "what" fields and fill statePrimaryKey's - * mField information using "state" fields. - */ -void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link, - HashableDimensionKey* statePrimaryKey); - -/** - * Returns true if the primaryKey values are a subset of the whatKey values. - * The values from the primaryKey come from the state atom, so we need to - * check that a link exists between the state atom field and what atom field. - * - * Example: - * whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}] - * statePrimaryKey = [Atom: 27, {uid: 1005}] - * Returns true IF one of the Metric2State links Atom 10's uid to Atom 27's uid - * - * Example: - * whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}] - * statePrimaryKey = [Atom: 59, {uid: 1005, package_name: "system"}] - * Returns false - */ -bool containsLinkedStateValues(const HashableDimensionKey& whatKey, - const HashableDimensionKey& primaryKey, - const std::vector<Metric2State>& stateLinks, - const int32_t stateAtomId); - -/** - * Returns true if there is a Metric2State link that links the stateField and - * the metricField (they are equal fields from different atoms). - */ -bool linked(const std::vector<Metric2State>& stateLinks, const int32_t stateAtomId, - const Field& stateField, const Field& metricField); -} // namespace statsd -} // namespace os -} // namespace android - -namespace std { - -using android::os::statsd::HashableDimensionKey; -using android::os::statsd::MetricDimensionKey; - -template <> -struct hash<HashableDimensionKey> { - std::size_t operator()(const HashableDimensionKey& key) const { - return hashDimension(key); - } -}; - -template <> -struct hash<MetricDimensionKey> { - std::size_t operator()(const MetricDimensionKey& key) const { - android::hash_t hash = hashDimension(key.getDimensionKeyInWhat()); - hash = android::JenkinsHashMix(hash, hashDimension(key.getStateValuesKey())); - return android::JenkinsHashWhiten(hash); - } -}; -} // namespace std diff --git a/cmds/statsd/src/Log.h b/cmds/statsd/src/Log.h deleted file mode 100644 index 87f4cbaf02f6..000000000000 --- a/cmds/statsd/src/Log.h +++ /dev/null @@ -1,33 +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. - */ - -/* - * This file must be included at the top of the file. Other header files - * occasionally include log.h, and if LOG_TAG isn't set when that happens - * we'll get a preprocesser error when we try to define it here. - */ - -#pragma once - -#define LOG_TAG "statsd" - -#include <log/log.h> - -// Use the local value to turn on/off debug logs instead of using log.tag. properties. -// The advantage is that in production compiler can remove the logging code if the local -// DEBUG/VERBOSE is false. -#define VLOG(...) \ - if (DEBUG) ALOGD(__VA_ARGS__); diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp deleted file mode 100644 index 6eff639aca92..000000000000 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ /dev/null @@ -1,1145 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsLogProcessor.h" - -#include <android-base/file.h> -#include <cutils/multiuser.h> -#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> -#include <frameworks/base/cmds/statsd/src/experiment_ids.pb.h> - -#include "android-base/stringprintf.h" -#include "external/StatsPullerManager.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "metrics/CountMetricProducer.h" -#include "StatsService.h" -#include "state/StateManager.h" -#include "stats_log_util.h" -#include "stats_util.h" -#include "statslog_statsd.h" -#include "storage/StorageManager.h" - -using namespace android; -using android::base::StringPrintf; -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -// for ConfigMetricsReportList -const int FIELD_ID_CONFIG_KEY = 1; -const int FIELD_ID_REPORTS = 2; -// for ConfigKey -const int FIELD_ID_UID = 1; -const int FIELD_ID_ID = 2; -// for ConfigMetricsReport -// const int FIELD_ID_METRICS = 1; // written in MetricsManager.cpp -const int FIELD_ID_UID_MAP = 2; -const int FIELD_ID_LAST_REPORT_ELAPSED_NANOS = 3; -const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4; -const int FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS = 5; -const int FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS = 6; -const int FIELD_ID_DUMP_REPORT_REASON = 8; -const int FIELD_ID_STRINGS = 9; - -// for ActiveConfigList -const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1; - -// for permissions checks -constexpr const char* kPermissionDump = "android.permission.DUMP"; -constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS"; - -#define NS_PER_HOUR 3600 * NS_PER_SEC - -#define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric" -#define STATS_METADATA_DIR "/data/misc/stats-metadata" - -// Cool down period for writing data to disk to avoid overwriting files. -#define WRITE_DATA_COOL_DOWN_SEC 5 - -StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor, - const int64_t timeBaseNs, - const std::function<bool(const ConfigKey&)>& sendBroadcast, - const std::function<bool( - const int&, const vector<int64_t>&)>& activateBroadcast) - : mUidMap(uidMap), - mPullerManager(pullerManager), - mAnomalyAlarmMonitor(anomalyAlarmMonitor), - mPeriodicAlarmMonitor(periodicAlarmMonitor), - mSendBroadcast(sendBroadcast), - mSendActivationBroadcast(activateBroadcast), - mTimeBaseNs(timeBaseNs), - mLargestTimestampSeen(0), - mLastTimestampSeen(0) { - mPullerManager->ForceClearPullerCache(); - StateManager::getInstance().updateLogSources(uidMap); -} - -StatsLogProcessor::~StatsLogProcessor() { -} - -static void flushProtoToBuffer(ProtoOutputStream& proto, vector<uint8_t>* outData) { - outData->clear(); - outData->resize(proto.size()); - size_t pos = 0; - sp<android::util::ProtoReader> reader = proto.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((*outData)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } -} - -void StatsLogProcessor::processFiredAnomalyAlarmsLocked( - const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) { - for (const auto& itr : mMetricsManagers) { - itr.second->onAnomalyAlarmFired(timestampNs, alarmSet); - } -} -void StatsLogProcessor::onPeriodicAlarmFired( - const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) { - - std::lock_guard<std::mutex> lock(mMetricsMutex); - for (const auto& itr : mMetricsManagers) { - itr.second->onPeriodicAlarmFired(timestampNs, alarmSet); - } -} - -void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const { - if (std::pair<int, int> indexRange; event->hasAttributionChain(&indexRange)) { - vector<FieldValue>* const fieldValues = event->getMutableValues(); - for (int i = indexRange.first; i <= indexRange.second; i++) { - FieldValue& fieldValue = fieldValues->at(i); - if (isAttributionUidField(fieldValue)) { - const int hostUid = mUidMap->getHostUidOrSelf(fieldValue.mValue.int_value); - fieldValue.mValue.setInt(hostUid); - } - } - } else { - int uidFieldIndex = event->getUidFieldIndex(); - if (uidFieldIndex != -1) { - Value& value = (*event->getMutableValues())[uidFieldIndex].mValue; - const int hostUid = mUidMap->getHostUidOrSelf(value.int_value); - value.setInt(hostUid); - } - } -} - -void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) { - status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR; - bool is_create = event.GetBool(3, &err); - auto parent_uid = int(event.GetLong(1, &err2)); - auto isolated_uid = int(event.GetLong(2, &err3)); - if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) { - if (is_create) { - mUidMap->assignIsolatedUid(isolated_uid, parent_uid); - } else { - mUidMap->removeIsolatedUid(isolated_uid); - } - } else { - ALOGE("Failed to parse uid in the isolated uid change event."); - } -} - -void StatsLogProcessor::onBinaryPushStateChangedEventLocked(LogEvent* event) { - pid_t pid = event->GetPid(); - uid_t uid = event->GetUid(); - if (!checkPermissionForIds(kPermissionDump, pid, uid) || - !checkPermissionForIds(kPermissionUsage, pid, uid)) { - return; - } - // The Get* functions don't modify the status on success, they only write in - // failure statuses, so we can use one status variable for all calls then - // check if it is no longer NO_ERROR. - status_t err = NO_ERROR; - InstallTrainInfo trainInfo; - trainInfo.trainName = string(event->GetString(1 /*train name field id*/, &err)); - trainInfo.trainVersionCode = event->GetLong(2 /*train version field id*/, &err); - trainInfo.requiresStaging = event->GetBool(3 /*requires staging field id*/, &err); - trainInfo.rollbackEnabled = event->GetBool(4 /*rollback enabled field id*/, &err); - trainInfo.requiresLowLatencyMonitor = - event->GetBool(5 /*requires low latency monitor field id*/, &err); - trainInfo.status = int32_t(event->GetLong(6 /*state field id*/, &err)); - std::vector<uint8_t> trainExperimentIdBytes = - event->GetStorage(7 /*experiment ids field id*/, &err); - bool is_rollback = event->GetBool(10 /*is rollback field id*/, &err); - - if (err != NO_ERROR) { - ALOGE("Failed to parse fields in binary push state changed log event"); - return; - } - ExperimentIds trainExperimentIds; - if (!trainExperimentIds.ParseFromArray(trainExperimentIdBytes.data(), - trainExperimentIdBytes.size())) { - ALOGE("Failed to parse experimentids in binary push state changed."); - return; - } - trainInfo.experimentIds = {trainExperimentIds.experiment_id().begin(), - trainExperimentIds.experiment_id().end()}; - - // Update the train info on disk and get any data the logevent is missing. - getAndUpdateTrainInfoOnDisk(is_rollback, &trainInfo); - - std::vector<uint8_t> trainExperimentIdProto; - writeExperimentIdsToProto(trainInfo.experimentIds, &trainExperimentIdProto); - int32_t userId = multiuser_get_user_id(uid); - - event->updateValue(2 /*train version field id*/, trainInfo.trainVersionCode, LONG); - event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STORAGE); - event->updateValue(8 /*user id field id*/, userId, INT); - - // If this event is a rollback event, then the following bits in the event - // are invalid and we will need to update them with the values we pulled - // from disk. - if (is_rollback) { - int bit = trainInfo.requiresStaging ? 1 : 0; - event->updateValue(3 /*requires staging field id*/, bit, INT); - bit = trainInfo.rollbackEnabled ? 1 : 0; - event->updateValue(4 /*rollback enabled field id*/, bit, INT); - bit = trainInfo.requiresLowLatencyMonitor ? 1 : 0; - event->updateValue(5 /*requires low latency monitor field id*/, bit, INT); - } -} - -void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(bool is_rollback, - InstallTrainInfo* trainInfo) { - // If the train name is empty, we don't know which train to attribute the - // event to, so return early. - if (trainInfo->trainName.empty()) { - return; - } - bool readTrainInfoSuccess = false; - InstallTrainInfo trainInfoOnDisk; - readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo->trainName, trainInfoOnDisk); - - bool resetExperimentIds = false; - if (readTrainInfoSuccess) { - // Keep the old train version if we received an empty version. - if (trainInfo->trainVersionCode == -1) { - trainInfo->trainVersionCode = trainInfoOnDisk.trainVersionCode; - } else if (trainInfo->trainVersionCode != trainInfoOnDisk.trainVersionCode) { - // Reset experiment ids if we receive a new non-empty train version. - resetExperimentIds = true; - } - - // Reset if we received a different experiment id. - if (!trainInfo->experimentIds.empty() && - (trainInfoOnDisk.experimentIds.empty() || - trainInfo->experimentIds.at(0) != trainInfoOnDisk.experimentIds[0])) { - resetExperimentIds = true; - } - } - - // Find the right experiment IDs - if ((!resetExperimentIds || is_rollback) && readTrainInfoSuccess) { - trainInfo->experimentIds = trainInfoOnDisk.experimentIds; - } - - if (!trainInfo->experimentIds.empty()) { - int64_t firstId = trainInfo->experimentIds.at(0); - auto& ids = trainInfo->experimentIds; - switch (trainInfo->status) { - 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::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::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); - } - break; - } - } - - // If this event is a rollback event, the following fields are invalid and - // need to be replaced by the fields stored to disk. - if (is_rollback) { - trainInfo->requiresStaging = trainInfoOnDisk.requiresStaging; - trainInfo->rollbackEnabled = trainInfoOnDisk.rollbackEnabled; - trainInfo->requiresLowLatencyMonitor = trainInfoOnDisk.requiresLowLatencyMonitor; - } - - StorageManager::writeTrainInfo(*trainInfo); -} - -void StatsLogProcessor::onWatchdogRollbackOccurredLocked(LogEvent* event) { - pid_t pid = event->GetPid(); - uid_t uid = event->GetUid(); - if (!checkPermissionForIds(kPermissionDump, pid, uid) || - !checkPermissionForIds(kPermissionUsage, pid, uid)) { - return; - } - // The Get* functions don't modify the status on success, they only write in - // failure statuses, so we can use one status variable for all calls then - // check if it is no longer NO_ERROR. - status_t err = NO_ERROR; - int32_t rollbackType = int32_t(event->GetInt(1 /*rollback type field id*/, &err)); - string packageName = string(event->GetString(2 /*package name field id*/, &err)); - - if (err != NO_ERROR) { - ALOGE("Failed to parse fields in watchdog rollback occurred log event"); - return; - } - - vector<int64_t> experimentIds = - processWatchdogRollbackOccurred(rollbackType, packageName); - vector<uint8_t> experimentIdProto; - writeExperimentIdsToProto(experimentIds, &experimentIdProto); - - event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STORAGE); -} - -vector<int64_t> StatsLogProcessor::processWatchdogRollbackOccurred(const int32_t rollbackTypeIn, - const string& packageNameIn) { - // If the package name is empty, we can't attribute it to any train, so - // return early. - if (packageNameIn.empty()) { - return vector<int64_t>(); - } - bool readTrainInfoSuccess = false; - InstallTrainInfo trainInfoOnDisk; - // We use the package name of the event as the train name. - readTrainInfoSuccess = StorageManager::readTrainInfo(packageNameIn, trainInfoOnDisk); - - if (!readTrainInfoSuccess) { - return vector<int64_t>(); - } - - if (trainInfoOnDisk.experimentIds.empty()) { - return vector<int64_t>(); - } - - int64_t firstId = trainInfoOnDisk.experimentIds[0]; - auto& ids = trainInfoOnDisk.experimentIds; - switch (rollbackTypeIn) { - 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::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); - } - StorageManager::writeTrainInfo(trainInfoOnDisk); - break; - } - - return trainInfoOnDisk.experimentIds; -} - -void StatsLogProcessor::resetConfigs() { - std::lock_guard<std::mutex> lock(mMetricsMutex); - resetConfigsLocked(getElapsedRealtimeNs()); -} - -void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs) { - std::vector<ConfigKey> configKeys; - for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) { - configKeys.push_back(it->first); - } - resetConfigsLocked(timestampNs, configKeys); -} - -void StatsLogProcessor::OnLogEvent(LogEvent* event) { - OnLogEvent(event, getElapsedRealtimeNs()); -} - -void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - - // Tell StatsdStats about new event - const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs(); - int atomId = event->GetTagId(); - StatsdStats::getInstance().noteAtomLogged(atomId, eventElapsedTimeNs / NS_PER_SEC); - if (!event->isValid()) { - StatsdStats::getInstance().noteAtomError(atomId); - return; - } - - // Hard-coded logic to update train info on disk and fill in any information - // this log event may be missing. - if (atomId == 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 (atomId == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) { - onWatchdogRollbackOccurredLocked(event); - } - - if (mPrintAllLogs) { - ALOGI("%s", event->ToString().c_str()); - } - resetIfConfigTtlExpiredLocked(eventElapsedTimeNs); - - // 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 (atomId == android::os::statsd::util::ISOLATED_UID_CHANGED) { - onIsolatedUidChangedEventLocked(*event); - } else { - // Map the isolated uid to host uid if necessary. - mapIsolatedUidToHostUidIfNecessaryLocked(event); - } - - StateManager::getInstance().onLogEvent(*event); - - if (mMetricsManagers.empty()) { - return; - } - - bool fireAlarm = false; - { - std::lock_guard<std::mutex> anomalyLock(mAnomalyAlarmMutex); - if (mNextAnomalyAlarmTime != 0 && - MillisToNano(mNextAnomalyAlarmTime) <= elapsedRealtimeNs) { - mNextAnomalyAlarmTime = 0; - VLOG("informing anomaly alarm at time %lld", (long long)elapsedRealtimeNs); - fireAlarm = true; - } - } - if (fireAlarm) { - informAnomalyAlarmFiredLocked(NanoToMillis(elapsedRealtimeNs)); - } - - int64_t curTimeSec = getElapsedRealtimeSec(); - if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) { - mPullerManager->ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC); - mLastPullerCacheClearTimeSec = curTimeSec; - } - - std::unordered_set<int> uidsWithActiveConfigsChanged; - std::unordered_map<int, std::vector<int64_t>> activeConfigsPerUid; - // pass the event to metrics managers. - for (auto& pair : mMetricsManagers) { - int uid = pair.first.GetUid(); - int64_t configId = pair.first.GetId(); - bool isPrevActive = pair.second->isActive(); - pair.second->onLogEvent(*event); - bool isCurActive = pair.second->isActive(); - // Map all active configs by uid. - if (isCurActive) { - auto activeConfigs = activeConfigsPerUid.find(uid); - if (activeConfigs != activeConfigsPerUid.end()) { - activeConfigs->second.push_back(configId); - } else { - vector<int64_t> newActiveConfigs; - newActiveConfigs.push_back(configId); - activeConfigsPerUid[uid] = newActiveConfigs; - } - } - // The activation state of this config changed. - if (isPrevActive != isCurActive) { - VLOG("Active status changed for uid %d", uid); - uidsWithActiveConfigsChanged.insert(uid); - StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive); - } - flushIfNecessaryLocked(pair.first, *(pair.second)); - } - - // Don't use the event timestamp for the guardrail. - for (int uid : uidsWithActiveConfigsChanged) { - // Send broadcast so that receivers can pull data. - auto lastBroadcastTime = mLastActivationBroadcastTimes.find(uid); - if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) { - if (elapsedRealtimeNs - lastBroadcastTime->second < - StatsdStats::kMinActivationBroadcastPeriodNs) { - StatsdStats::getInstance().noteActivationBroadcastGuardrailHit(uid); - VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us."); - return; - } - } - auto activeConfigs = activeConfigsPerUid.find(uid); - if (activeConfigs != activeConfigsPerUid.end()) { - if (mSendActivationBroadcast(uid, activeConfigs->second)) { - VLOG("StatsD sent activation notice for uid %d", uid); - mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs; - } - } else { - std::vector<int64_t> emptyActiveConfigs; - if (mSendActivationBroadcast(uid, emptyActiveConfigs)) { - VLOG("StatsD sent EMPTY activation notice for uid %d", uid); - mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs; - } - } - } -} - -void StatsLogProcessor::GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - GetActiveConfigsLocked(uid, outActiveConfigs); -} - -void StatsLogProcessor::GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs) { - outActiveConfigs.clear(); - for (auto& pair : mMetricsManagers) { - if (pair.first.GetUid() == uid && pair.second->isActive()) { - outActiveConfigs.push_back(pair.first.GetId()); - } - } -} - -void StatsLogProcessor::OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config, bool modularUpdate) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - WriteDataToDiskLocked(key, timestampNs, CONFIG_UPDATED, NO_TIME_CONSTRAINTS); - OnConfigUpdatedLocked(timestampNs, key, config, modularUpdate); -} - -void StatsLogProcessor::OnConfigUpdatedLocked(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config, bool modularUpdate) { - VLOG("Updated configuration for key %s", key.ToString().c_str()); - // Create new config if this is not a modular update or if this is a new config. - const auto& it = mMetricsManagers.find(key); - bool configValid = false; - if (!modularUpdate || it == mMetricsManagers.end()) { - sp<MetricsManager> newMetricsManager = - new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager, - mAnomalyAlarmMonitor, mPeriodicAlarmMonitor); - configValid = newMetricsManager->isConfigValid(); - if (configValid) { - newMetricsManager->init(); - mUidMap->OnConfigUpdated(key); - newMetricsManager->refreshTtl(timestampNs); - mMetricsManagers[key] = newMetricsManager; - VLOG("StatsdConfig valid"); - } - } else { - // Preserve the existing MetricsManager, update necessary components and metadata in place. - configValid = it->second->updateConfig(config, mTimeBaseNs, timestampNs, - mAnomalyAlarmMonitor, mPeriodicAlarmMonitor); - if (configValid) { - mUidMap->OnConfigUpdated(key); - } - } - if (!configValid) { - // If there is any error in the config, don't use it. - // Remove any existing config with the same key. - ALOGE("StatsdConfig NOT valid"); - mMetricsManagers.erase(key); - } -} - -size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const { - std::lock_guard<std::mutex> lock(mMetricsMutex); - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - ALOGW("Config source %s does not exist", key.ToString().c_str()); - return 0; - } - return it->second->byteSize(); -} - -void StatsLogProcessor::dumpStates(int out, bool verbose) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - FILE* fout = fdopen(out, "w"); - if (fout == NULL) { - return; - } - fprintf(fout, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size()); - for (auto metricsManager : mMetricsManagers) { - metricsManager.second->dumpStates(fout, verbose); - } - - fclose(fout); -} - -/* - * onDumpReport dumps serialized ConfigMetricsReportList into proto. - */ -void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency, - ProtoOutputStream* proto) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - - // Start of ConfigKey. - uint64_t configKeyToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid()); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId()); - proto->end(configKeyToken); - // End of ConfigKey. - - bool keepFile = false; - auto it = mMetricsManagers.find(key); - if (it != mMetricsManagers.end() && it->second->shouldPersistLocalHistory()) { - keepFile = true; - } - - // Then, check stats-data directory to see there's any file containing - // ConfigMetricsReport from previous shutdowns to concatenate to reports. - StorageManager::appendConfigMetricsReport( - key, proto, erase_data && !keepFile /* should remove file after appending it */, - dumpReportReason == ADB_DUMP /*if caller is adb*/); - - if (it != mMetricsManagers.end()) { - // This allows another broadcast to be sent within the rate-limit period if we get close to - // filling the buffer again soon. - mLastBroadcastTimes.erase(key); - - vector<uint8_t> buffer; - onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket, - erase_data, dumpReportReason, dumpLatency, - false /* is this data going to be saved on disk */, &buffer); - proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS, - reinterpret_cast<char*>(buffer.data()), buffer.size()); - } else { - ALOGW("Config source %s does not exist", key.ToString().c_str()); - } -} - -/* - * onDumpReport dumps serialized ConfigMetricsReportList into outData. - */ -void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency, - vector<uint8_t>* outData) { - ProtoOutputStream proto; - onDumpReport(key, dumpTimeStampNs, include_current_partial_bucket, erase_data, - dumpReportReason, dumpLatency, &proto); - - if (outData != nullptr) { - flushProtoToBuffer(proto, outData); - VLOG("output data size %zu", outData->size()); - } - - StatsdStats::getInstance().noteMetricsReportSent(key, proto.size()); -} - -/* - * onConfigMetricsReportLocked dumps serialized ConfigMetricsReport into outData. - */ -void StatsLogProcessor::onConfigMetricsReportLocked( - const ConfigKey& key, const int64_t dumpTimeStampNs, - const bool include_current_partial_bucket, const bool erase_data, - const DumpReportReason dumpReportReason, const DumpLatency dumpLatency, - const bool dataSavedOnDisk, vector<uint8_t>* buffer) { - // We already checked whether key exists in mMetricsManagers in - // WriteDataToDisk. - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - return; - } - int64_t lastReportTimeNs = it->second->getLastReportTimeNs(); - int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs(); - - std::set<string> str_set; - - ProtoOutputStream tempProto; - // First, fill in ConfigMetricsReport using current data on memory, which - // starts from filling in StatsLogReport's. - it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data, - dumpLatency, &str_set, &tempProto); - - // Fill in UidMap if there is at least one metric to report. - // This skips the uid map if it's an empty config. - if (it->second->getNumMetrics() > 0) { - uint64_t uidMapToken = tempProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP); - mUidMap->appendUidMap( - dumpTimeStampNs, key, it->second->hashStringInReport() ? &str_set : nullptr, - it->second->versionStringsInReport(), it->second->installerInReport(), &tempProto); - tempProto.end(uidMapToken); - } - - // Fill in the timestamps. - tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS, - (long long)lastReportTimeNs); - tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS, - (long long)dumpTimeStampNs); - tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS, - (long long)lastReportWallClockNs); - tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS, - (long long)getWallClockNs()); - // Dump report reason - tempProto.write(FIELD_TYPE_INT32 | FIELD_ID_DUMP_REPORT_REASON, dumpReportReason); - - for (const auto& str : str_set) { - tempProto.write(FIELD_TYPE_STRING | FIELD_COUNT_REPEATED | FIELD_ID_STRINGS, str); - } - - flushProtoToBuffer(tempProto, buffer); - - // save buffer to disk if needed - if (erase_data && !dataSavedOnDisk && it->second->shouldPersistLocalHistory()) { - VLOG("save history to disk"); - string file_name = StorageManager::getDataHistoryFileName((long)getWallClockSec(), - key.GetUid(), key.GetId()); - StorageManager::writeFile(file_name.c_str(), buffer->data(), buffer->size()); - } -} - -void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs, - const std::vector<ConfigKey>& configs) { - for (const auto& key : configs) { - StatsdConfig config; - if (StorageManager::readConfigFromDisk(key, &config)) { - // Force a full update when resetting a config. - OnConfigUpdatedLocked(timestampNs, key, config, /*modularUpdate=*/false); - StatsdStats::getInstance().noteConfigReset(key); - } else { - ALOGE("Failed to read backup config from disk for : %s", key.ToString().c_str()); - auto it = mMetricsManagers.find(key); - if (it != mMetricsManagers.end()) { - it->second->refreshTtl(timestampNs); - } - } - } -} - -void StatsLogProcessor::resetIfConfigTtlExpiredLocked(const int64_t timestampNs) { - std::vector<ConfigKey> configKeysTtlExpired; - for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) { - if (it->second != nullptr && !it->second->isInTtl(timestampNs)) { - configKeysTtlExpired.push_back(it->first); - } - } - if (configKeysTtlExpired.size() > 0) { - WriteDataToDiskLocked(CONFIG_RESET, NO_TIME_CONSTRAINTS); - resetConfigsLocked(timestampNs, configKeysTtlExpired); - } -} - -void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - auto it = mMetricsManagers.find(key); - if (it != mMetricsManagers.end()) { - WriteDataToDiskLocked(key, getElapsedRealtimeNs(), CONFIG_REMOVED, - NO_TIME_CONSTRAINTS); - mMetricsManagers.erase(it); - mUidMap->OnConfigRemoved(key); - } - StatsdStats::getInstance().noteConfigRemoved(key); - - mLastBroadcastTimes.erase(key); - - int uid = key.GetUid(); - bool lastConfigForUid = true; - for (auto it : mMetricsManagers) { - if (it.first.GetUid() == uid) { - lastConfigForUid = false; - break; - } - } - if (lastConfigForUid) { - mLastActivationBroadcastTimes.erase(uid); - } - - if (mMetricsManagers.empty()) { - mPullerManager->ForceClearPullerCache(); - } -} - -void StatsLogProcessor::flushIfNecessaryLocked(const ConfigKey& key, - MetricsManager& metricsManager) { - int64_t elapsedRealtimeNs = getElapsedRealtimeNs(); - auto lastCheckTime = mLastByteSizeTimes.find(key); - if (lastCheckTime != mLastByteSizeTimes.end()) { - if (elapsedRealtimeNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) { - return; - } - } - - // We suspect that the byteSize() computation is expensive, so we set a rate limit. - size_t totalBytes = metricsManager.byteSize(); - mLastByteSizeTimes[key] = elapsedRealtimeNs; - bool requestDump = false; - if (totalBytes > StatsdStats::kMaxMetricsBytesPerConfig) { - // Too late. We need to start clearing data. - metricsManager.dropData(elapsedRealtimeNs); - StatsdStats::getInstance().noteDataDropped(key, totalBytes); - VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str()); - } else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) || - (mOnDiskDataConfigs.find(key) != mOnDiskDataConfigs.end())) { - // Request to send a broadcast if: - // 1. in memory data > threshold OR - // 2. config has old data report on disk. - requestDump = true; - } - - if (requestDump) { - // Send broadcast so that receivers can pull data. - auto lastBroadcastTime = mLastBroadcastTimes.find(key); - if (lastBroadcastTime != mLastBroadcastTimes.end()) { - if (elapsedRealtimeNs - lastBroadcastTime->second < - StatsdStats::kMinBroadcastPeriodNs) { - VLOG("StatsD would've sent a broadcast but the rate limit stopped us."); - return; - } - } - if (mSendBroadcast(key)) { - mOnDiskDataConfigs.erase(key); - VLOG("StatsD triggered data fetch for %s", key.ToString().c_str()); - mLastBroadcastTimes[key] = elapsedRealtimeNs; - StatsdStats::getInstance().noteBroadcastSent(key); - } - } -} - -void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key, - const int64_t timestampNs, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency) { - if (mMetricsManagers.find(key) == mMetricsManagers.end() || - !mMetricsManagers.find(key)->second->shouldWriteToDisk()) { - return; - } - vector<uint8_t> buffer; - onConfigMetricsReportLocked(key, timestampNs, true /* include_current_partial_bucket*/, - true /* erase_data */, dumpReportReason, dumpLatency, true, - &buffer); - string file_name = - StorageManager::getDataFileName((long)getWallClockSec(), key.GetUid(), key.GetId()); - StorageManager::writeFile(file_name.c_str(), buffer.data(), buffer.size()); - - // We were able to write the ConfigMetricsReport to disk, so we should trigger collection ASAP. - mOnDiskDataConfigs.insert(key); -} - -void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - const int64_t timeNs = getElapsedRealtimeNs(); - // Do not write to disk if we already have in the last few seconds. - if (static_cast<unsigned long long> (timeNs) < - mLastActiveMetricsWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) { - ALOGI("Statsd skipping writing active metrics to disk. Already wrote data in last %d seconds", - WRITE_DATA_COOL_DOWN_SEC); - return; - } - mLastActiveMetricsWriteNs = timeNs; - - ProtoOutputStream proto; - WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, DEVICE_SHUTDOWN, &proto); - - string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); - StorageManager::deleteFile(file_name.c_str()); - android::base::unique_fd fd( - open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)); - if (fd == -1) { - ALOGE("Attempt to write %s but failed", file_name.c_str()); - return; - } - proto.flush(fd.get()); -} - -void StatsLogProcessor::SaveMetadataToDisk(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - // Do not write to disk if we already have in the last few seconds. - if (static_cast<unsigned long long> (systemElapsedTimeNs) < - mLastMetadataWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) { - ALOGI("Statsd skipping writing metadata to disk. Already wrote data in last %d seconds", - WRITE_DATA_COOL_DOWN_SEC); - return; - } - mLastMetadataWriteNs = systemElapsedTimeNs; - - metadata::StatsMetadataList metadataList; - WriteMetadataToProtoLocked( - currentWallClockTimeNs, systemElapsedTimeNs, &metadataList); - - string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR); - StorageManager::deleteFile(file_name.c_str()); - - if (metadataList.stats_metadata_size() == 0) { - // Skip the write if we have nothing to write. - return; - } - - std::string data; - metadataList.SerializeToString(&data); - StorageManager::writeFile(file_name.c_str(), data.c_str(), data.size()); -} - -void StatsLogProcessor::WriteMetadataToProto(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadataList* metadataList) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - WriteMetadataToProtoLocked(currentWallClockTimeNs, systemElapsedTimeNs, metadataList); -} - -void StatsLogProcessor::WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadataList* metadataList) { - for (const auto& pair : mMetricsManagers) { - const sp<MetricsManager>& metricsManager = pair.second; - metadata::StatsMetadata* statsMetadata = metadataList->add_stats_metadata(); - bool metadataWritten = metricsManager->writeMetadataToProto(currentWallClockTimeNs, - systemElapsedTimeNs, statsMetadata); - if (!metadataWritten) { - metadataList->mutable_stats_metadata()->RemoveLast(); - } - } -} - -void StatsLogProcessor::LoadMetadataFromDisk(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR); - int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); - if (-1 == fd) { - VLOG("Attempt to read %s but failed", file_name.c_str()); - StorageManager::deleteFile(file_name.c_str()); - return; - } - string content; - if (!android::base::ReadFdToString(fd, &content)) { - ALOGE("Attempt to read %s but failed", file_name.c_str()); - close(fd); - StorageManager::deleteFile(file_name.c_str()); - return; - } - - close(fd); - - metadata::StatsMetadataList statsMetadataList; - if (!statsMetadataList.ParseFromString(content)) { - ALOGE("Attempt to read %s but failed; failed to metadata", file_name.c_str()); - StorageManager::deleteFile(file_name.c_str()); - return; - } - SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs); - StorageManager::deleteFile(file_name.c_str()); -} - -void StatsLogProcessor::SetMetadataState(const metadata::StatsMetadataList& statsMetadataList, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs); -} - -void StatsLogProcessor::SetMetadataStateLocked( - const metadata::StatsMetadataList& statsMetadataList, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - for (const metadata::StatsMetadata& metadata : statsMetadataList.stats_metadata()) { - ConfigKey key(metadata.config_key().uid(), metadata.config_key().config_id()); - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - ALOGE("No config found for configKey %s", key.ToString().c_str()); - continue; - } - VLOG("Setting metadata %s", key.ToString().c_str()); - it->second->loadMetadata(metadata, currentWallClockTimeNs, systemElapsedTimeNs); - } - VLOG("Successfully loaded %d metadata.", statsMetadataList.stats_metadata_size()); -} - -void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, reason, proto); -} - -void StatsLogProcessor::WriteActiveConfigsToProtoOutputStreamLocked( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { - for (const auto& pair : mMetricsManagers) { - const sp<MetricsManager>& metricsManager = pair.second; - uint64_t configToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG); - metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, reason, proto); - proto->end(configToken); - } -} -void StatsLogProcessor::LoadActiveConfigsFromDisk() { - std::lock_guard<std::mutex> lock(mMetricsMutex); - string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); - int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); - if (-1 == fd) { - VLOG("Attempt to read %s but failed", file_name.c_str()); - StorageManager::deleteFile(file_name.c_str()); - return; - } - string content; - if (!android::base::ReadFdToString(fd, &content)) { - ALOGE("Attempt to read %s but failed", file_name.c_str()); - close(fd); - StorageManager::deleteFile(file_name.c_str()); - return; - } - - close(fd); - - ActiveConfigList activeConfigList; - if (!activeConfigList.ParseFromString(content)) { - ALOGE("Attempt to read %s but failed; failed to load active configs", file_name.c_str()); - StorageManager::deleteFile(file_name.c_str()); - return; - } - // Passing in mTimeBaseNs only works as long as we only load from disk is when statsd starts. - SetConfigsActiveStateLocked(activeConfigList, mTimeBaseNs); - StorageManager::deleteFile(file_name.c_str()); -} - -void StatsLogProcessor::SetConfigsActiveState(const ActiveConfigList& activeConfigList, - int64_t currentTimeNs) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - SetConfigsActiveStateLocked(activeConfigList, currentTimeNs); -} - -void StatsLogProcessor::SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList, - int64_t currentTimeNs) { - for (int i = 0; i < activeConfigList.config_size(); i++) { - const auto& config = activeConfigList.config(i); - ConfigKey key(config.uid(), config.id()); - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - ALOGE("No config found for config %s", key.ToString().c_str()); - continue; - } - VLOG("Setting active config %s", key.ToString().c_str()); - it->second->loadActiveConfig(config, currentTimeNs); - } - VLOG("Successfully loaded %d active configs.", activeConfigList.config_size()); -} - -void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency) { - const int64_t timeNs = getElapsedRealtimeNs(); - // Do not write to disk if we already have in the last few seconds. - // This is to avoid overwriting files that would have the same name if we - // write twice in the same second. - if (static_cast<unsigned long long> (timeNs) < - mLastWriteTimeNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) { - ALOGI("Statsd skipping writing data to disk. Already wrote data in last %d seconds", - WRITE_DATA_COOL_DOWN_SEC); - return; - } - mLastWriteTimeNs = timeNs; - for (auto& pair : mMetricsManagers) { - WriteDataToDiskLocked(pair.first, timeNs, dumpReportReason, dumpLatency); - } -} - -void StatsLogProcessor::WriteDataToDisk(const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - WriteDataToDiskLocked(dumpReportReason, dumpLatency); -} - -void StatsLogProcessor::informPullAlarmFired(const int64_t timestampNs) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - mPullerManager->OnAlarmFired(timestampNs); -} - -int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) { - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - return 0; - } else { - return it->second->getLastReportTimeNs(); - } -} - -void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, - const int uid, const int64_t version) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - VLOG("Received app upgrade"); - StateManager::getInstance().notifyAppChanged(apk, mUidMap); - for (const auto& it : mMetricsManagers) { - it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version); - } -} - -void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, - const int uid) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - VLOG("Received app removed"); - StateManager::getInstance().notifyAppChanged(apk, mUidMap); - for (const auto& it : mMetricsManagers) { - it.second->notifyAppRemoved(eventTimeNs, apk, uid); - } -} - -void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - VLOG("Received uid map"); - StateManager::getInstance().updateLogSources(mUidMap); - for (const auto& it : mMetricsManagers) { - it.second->onUidMapReceived(eventTimeNs); - } -} - -void StatsLogProcessor::onStatsdInitCompleted(const int64_t& elapsedTimeNs) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - VLOG("Received boot completed signal"); - for (const auto& it : mMetricsManagers) { - it.second->onStatsdInitCompleted(elapsedTimeNs); - } -} - -void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - mOnDiskDataConfigs.insert(key); -} - -void StatsLogProcessor::setAnomalyAlarm(const int64_t elapsedTimeMillis) { - std::lock_guard<std::mutex> lock(mAnomalyAlarmMutex); - mNextAnomalyAlarmTime = elapsedTimeMillis; -} - -void StatsLogProcessor::cancelAnomalyAlarm() { - std::lock_guard<std::mutex> lock(mAnomalyAlarmMutex); - mNextAnomalyAlarmTime = 0; -} - -void StatsLogProcessor::informAnomalyAlarmFiredLocked(const int64_t elapsedTimeMillis) { - VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called"); - std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet = - mAnomalyAlarmMonitor->popSoonerThan(static_cast<uint32_t>(elapsedTimeMillis / 1000)); - if (alarmSet.size() > 0) { - VLOG("Found periodic alarm fired."); - processFiredAnomalyAlarmsLocked(MillisToNano(elapsedTimeMillis), alarmSet); - } else { - ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled."); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h deleted file mode 100644 index 2af277ad1e5b..000000000000 --- a/cmds/statsd/src/StatsLogProcessor.h +++ /dev/null @@ -1,374 +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. - */ - -#pragma once - -#include <gtest/gtest_prod.h> -#include "config/ConfigListener.h" -#include "logd/LogEvent.h" -#include "metrics/MetricsManager.h" -#include "packages/UidMap.h" -#include "external/StatsPullerManager.h" - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" - -#include <stdio.h> -#include <unordered_map> - -namespace android { -namespace os { -namespace statsd { - - -class StatsLogProcessor : public ConfigListener, public virtual PackageInfoListener { -public: - StatsLogProcessor(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor, - const int64_t timeBaseNs, - const std::function<bool(const ConfigKey&)>& sendBroadcast, - const std::function<bool(const int&, - const vector<int64_t>&)>& sendActivationBroadcast); - virtual ~StatsLogProcessor(); - - void OnLogEvent(LogEvent* event); - - void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config, bool modularUpdate = false); - void OnConfigRemoved(const ConfigKey& key); - - size_t GetMetricsSize(const ConfigKey& key) const; - - void GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs); - - void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, - const bool include_current_partial_bucket, const bool erase_data, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency, - vector<uint8_t>* outData); - void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, - const bool include_current_partial_bucket, const bool erase_data, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency, - ProtoOutputStream* proto); - - /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */ - void onPeriodicAlarmFired( - const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet); - - /* Flushes data to disk. Data on memory will be gone after written to disk. */ - void WriteDataToDisk(const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency); - - /* Persist configs containing metrics with active activations to disk. */ - void SaveActiveConfigsToDisk(int64_t currentTimeNs); - - /* Writes the current active status/ttl for all configs and metrics to ProtoOutputStream. */ - void WriteActiveConfigsToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); - - /* Load configs containing metrics with active activations from disk. */ - void LoadActiveConfigsFromDisk(); - - /* Persist metadata for configs and metrics to disk. */ - void SaveMetadataToDisk(int64_t currentWallClockTimeNs, int64_t systemElapsedTimeNs); - - /* Writes the statsd metadata for all configs and metrics to StatsMetadataList. */ - void WriteMetadataToProto(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadataList* metadataList); - - /* Load stats metadata for configs and metrics from disk. */ - void LoadMetadataFromDisk(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs); - - /* Sets the metadata for all configs and metrics */ - void SetMetadataState(const metadata::StatsMetadataList& statsMetadataList, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs); - - /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */ - void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs); - - /* Notify all MetricsManagers of app upgrades */ - void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version) override; - - /* Notify all MetricsManagers of app removals */ - void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override; - - /* Notify all MetricsManagers of uid map snapshots received */ - void onUidMapReceived(const int64_t& eventTimeNs) override; - - /* Notify all metrics managers of boot completed - * This will force a bucket split when the boot is finished. - */ - void onStatsdInitCompleted(const int64_t& elapsedTimeNs); - - // Reset all configs. - void resetConfigs(); - - inline sp<UidMap> getUidMap() { - return mUidMap; - } - - void dumpStates(int outFd, bool verbose); - - void informPullAlarmFired(const int64_t timestampNs); - - int64_t getLastReportTimeNs(const ConfigKey& key); - - inline void setPrintLogs(bool enabled) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - mPrintAllLogs = enabled; - } - - // Add a specific config key to the possible configs to dump ASAP. - void noteOnDiskData(const ConfigKey& key); - - void setAnomalyAlarm(const int64_t timeMillis); - - void cancelAnomalyAlarm(); - -private: - // For testing only. - inline sp<AlarmMonitor> getAnomalyAlarmMonitor() const { - return mAnomalyAlarmMonitor; - } - - inline sp<AlarmMonitor> getPeriodicAlarmMonitor() const { - return mPeriodicAlarmMonitor; - } - - mutable mutex mMetricsMutex; - - // Guards mNextAnomalyAlarmTime. A separate mutex is needed because alarms are set/cancelled - // in the onLogEvent code path, which is locked by mMetricsMutex. - // DO NOT acquire mMetricsMutex while holding mAnomalyAlarmMutex. This can lead to a deadlock. - mutable mutex mAnomalyAlarmMutex; - - std::unordered_map<ConfigKey, sp<MetricsManager>> mMetricsManagers; - - std::unordered_map<ConfigKey, int64_t> mLastBroadcastTimes; - - // Last time we sent a broadcast to this uid that the active configs had changed. - std::unordered_map<int, int64_t> mLastActivationBroadcastTimes; - - // Tracks when we last checked the bytes consumed for each config key. - std::unordered_map<ConfigKey, int64_t> mLastByteSizeTimes; - - // Tracks which config keys has metric reports on disk - std::set<ConfigKey> mOnDiskDataConfigs; - - sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid. - - sp<StatsPullerManager> mPullerManager; // Reference to StatsPullerManager - - sp<AlarmMonitor> mAnomalyAlarmMonitor; - - sp<AlarmMonitor> mPeriodicAlarmMonitor; - - void OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs); - - void resetIfConfigTtlExpiredLocked(const int64_t timestampNs); - - void OnConfigUpdatedLocked(const int64_t currentTimestampNs, const ConfigKey& key, - const StatsdConfig& config, bool modularUpdate); - - void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs); - - void WriteActiveConfigsToProtoOutputStreamLocked( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); - - void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList, - int64_t currentTimeNs); - - void SetMetadataStateLocked(const metadata::StatsMetadataList& statsMetadataList, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs); - - void WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadataList* metadataList); - - void WriteDataToDiskLocked(const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency); - - void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency); - - void onConfigMetricsReportLocked( - const ConfigKey& key, const int64_t dumpTimeStampNs, - const bool include_current_partial_bucket, const bool erase_data, - const DumpReportReason dumpReportReason, const DumpLatency dumpLatency, - /*if dataSavedToDisk is true, it indicates the caller will write the data to disk - (e.g., before reboot). So no need to further persist local history.*/ - const bool dataSavedToDisk, vector<uint8_t>* proto); - - /* Check if we should send a broadcast if approaching memory limits and if we're over, we - * actually delete the data. */ - void flushIfNecessaryLocked(const ConfigKey& key, MetricsManager& metricsManager); - - // Maps the isolated uid in the log event to host uid if the log event contains uid fields. - void mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const; - - // Handler over the isolated uid change event. - void onIsolatedUidChangedEventLocked(const LogEvent& event); - - // Handler over the binary push state changed event. - void onBinaryPushStateChangedEventLocked(LogEvent* event); - - // Handler over the watchdog rollback occurred event. - void onWatchdogRollbackOccurredLocked(LogEvent* event); - - // Updates train info on disk based on binary push state changed info and - // write disk info into parameters. - void getAndUpdateTrainInfoOnDisk(bool is_rollback, InstallTrainInfo* trainInfoIn); - - // Gets experiment ids on disk for associated train and updates them - // depending on rollback type. Then writes them back to disk and returns - // them. - std::vector<int64_t> processWatchdogRollbackOccurred(const int32_t rollbackTypeIn, - const string& packageName); - - // Reset all configs. - void resetConfigsLocked(const int64_t timestampNs); - // Reset the specified configs. - void resetConfigsLocked(const int64_t timestampNs, const std::vector<ConfigKey>& configs); - - // An anomaly alarm should have fired. - // Check with anomaly alarm manager to find the alarms and process the result. - void informAnomalyAlarmFiredLocked(const int64_t elapsedTimeMillis); - - /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */ - void processFiredAnomalyAlarmsLocked( - const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet); - - // Function used to send a broadcast so that receiver for the config key can call getData - // to retrieve the stored data. - std::function<bool(const ConfigKey& key)> mSendBroadcast; - - // Function used to send a broadcast so that receiver can be notified of which configs - // are currently active. - std::function<bool(const int& uid, const vector<int64_t>& configIds)> mSendActivationBroadcast; - - const int64_t mTimeBaseNs; - - // Largest timestamp of the events that we have processed. - int64_t mLargestTimestampSeen = 0; - - int64_t mLastTimestampSeen = 0; - - int64_t mLastPullerCacheClearTimeSec = 0; - - // Last time we wrote data to disk. - int64_t mLastWriteTimeNs = 0; - - // Last time we wrote active metrics to disk. - int64_t mLastActiveMetricsWriteNs = 0; - - //Last time we wrote metadata to disk. - int64_t mLastMetadataWriteNs = 0; - - // The time for the next anomaly alarm for alerts. - int64_t mNextAnomalyAlarmTime = 0; - - bool mPrintAllLogs = false; - - FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs); - FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize); - FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); - FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); - FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved); - FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); - FRIEND_TEST(StatsLogProcessorTest, - TestActivationOnBootMultipleActivationsDifferentActivationTypes); - FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); - - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3); - FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1); - FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2); - FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid); - FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain); - FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition); - FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents); - - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); - - FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); - FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); - - FRIEND_TEST(ConfigUpdateE2eTest, TestHashStrings); - FRIEND_TEST(ConfigUpdateE2eTest, TestUidMapVersionStringInstaller); - FRIEND_TEST(ConfigUpdateE2eTest, TestConfigTtl); - - FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges); - FRIEND_TEST(CountMetricE2eTest, TestSlicedState); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); - FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields); - - FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); - FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); - FRIEND_TEST(DurationMetricE2eTest, TestWithCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped); - FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset); - - FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp deleted file mode 100644 index 68c2dd56ef13..000000000000 --- a/cmds/statsd/src/StatsService.cpp +++ /dev/null @@ -1,1312 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsService.h" -#include "stats_log_util.h" -#include "android-base/stringprintf.h" -#include "config/ConfigKey.h" -#include "config/ConfigManager.h" -#include "guardrail/StatsdStats.h" -#include "storage/StorageManager.h" -#include "subscriber/SubscriberReporter.h" - -#include <android-base/file.h> -#include <android-base/strings.h> -#include <cutils/multiuser.h> -#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_statsd.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/system_properties.h> -#include <unistd.h> -#include <utils/String16.h> - -using namespace android; - -using android::base::StringPrintf; -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_MESSAGE; - -using Status = ::ndk::ScopedAStatus; - -namespace android { -namespace os { -namespace statsd { - -constexpr const char* kPermissionDump = "android.permission.DUMP"; - -constexpr const char* kPermissionRegisterPullAtom = "android.permission.REGISTER_STATS_PULL_ATOM"; - -#define STATS_SERVICE_DIR "/data/misc/stats-service" - -// for StatsDataDumpProto -const int FIELD_ID_REPORTS_LIST = 1; - -static Status exception(int32_t code, const std::string& msg) { - ALOGE("%s (%d)", msg.c_str(), code); - return Status::fromExceptionCodeWithMessage(code, msg.c_str()); -} - -static bool checkPermission(const char* permission) { - pid_t pid = AIBinder_getCallingPid(); - uid_t uid = AIBinder_getCallingUid(); - return checkPermissionForIds(permission, pid, uid); -} - -Status checkUid(uid_t expectedUid) { - uid_t uid = AIBinder_getCallingUid(); - if (uid == expectedUid || uid == AID_ROOT) { - return Status::ok(); - } else { - return exception(EX_SECURITY, - StringPrintf("UID %d is not expected UID %d", uid, expectedUid)); - } -} - -#define ENFORCE_UID(uid) { \ - Status status = checkUid((uid)); \ - if (!status.isOk()) { \ - return status; \ - } \ -} - -StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQueue> queue) - : mAnomalyAlarmMonitor(new AlarmMonitor( - MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, - [this](const shared_ptr<IStatsCompanionService>& /*sc*/, int64_t timeMillis) { - mProcessor->setAnomalyAlarm(timeMillis); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - }, - [this](const shared_ptr<IStatsCompanionService>& /*sc*/) { - mProcessor->cancelAnomalyAlarm(); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - })), - mPeriodicAlarmMonitor(new AlarmMonitor( - MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, - [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) { - if (sc != nullptr) { - sc->setAlarmForSubscriberTriggering(timeMillis); - StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); - } - }, - [](const shared_ptr<IStatsCompanionService>& sc) { - if (sc != nullptr) { - sc->cancelAlarmForSubscriberTriggering(); - StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); - } - })), - mEventQueue(queue), - mBootCompleteTrigger({kBootCompleteTag, kUidMapReceivedTag, kAllPullersRegisteredTag}, - [this]() { mProcessor->onStatsdInitCompleted(getElapsedRealtimeNs()); }), - mStatsCompanionServiceDeathRecipient( - AIBinder_DeathRecipient_new(StatsService::statsCompanionServiceDied)) { - mUidMap = UidMap::getInstance(); - mPullerManager = new StatsPullerManager(); - StatsPuller::SetUidMap(mUidMap); - mConfigManager = new ConfigManager(); - mProcessor = new StatsLogProcessor( - mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor, - getElapsedRealtimeNs(), - [this](const ConfigKey& key) { - shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key); - if (receiver == nullptr) { - VLOG("Could not find a broadcast receiver for %s", key.ToString().c_str()); - return false; - } else if (receiver->sendDataBroadcast( - mProcessor->getLastReportTimeNs(key)).isOk()) { - return true; - } else { - VLOG("Failed to send a broadcast for receiver %s", key.ToString().c_str()); - return false; - } - }, - [this](const int& uid, const vector<int64_t>& activeConfigs) { - shared_ptr<IPendingIntentRef> receiver = - mConfigManager->GetActiveConfigsChangedReceiver(uid); - if (receiver == nullptr) { - VLOG("Could not find receiver for uid %d", uid); - return false; - } else if (receiver->sendActiveConfigsChangedBroadcast(activeConfigs).isOk()) { - VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid); - return true; - } else { - VLOG("StatsService::active configs broadcast failed for uid %d" , uid); - return false; - } - }); - - mUidMap->setListener(mProcessor); - mConfigManager->AddListener(mProcessor); - - init_system_properties(); - - if (mEventQueue != nullptr) { - std::thread pushedEventThread([this] { readLogs(); }); - pushedEventThread.detach(); - } -} - -StatsService::~StatsService() { -} - -/* Runs on a dedicated thread to process pushed events. */ -void StatsService::readLogs() { - // Read forever..... long live statsd - while (1) { - // Block until an event is available. - auto event = mEventQueue->waitPop(); - // Pass it to StatsLogProcess to all configs/metrics - // At this point, the LogEventQueue is not blocked, so that the socketListener - // can read events from the socket and write to buffer to avoid data drop. - mProcessor->OnLogEvent(event.get()); - // The ShellSubscriber is only used by shell for local debugging. - if (mShellSubscriber != nullptr) { - mShellSubscriber->onLogEvent(*event); - } - } -} - -void StatsService::init_system_properties() { - mEngBuild = false; - const prop_info* buildType = __system_property_find("ro.build.type"); - if (buildType != NULL) { - __system_property_read_callback(buildType, init_build_type_callback, this); - } -} - -void StatsService::init_build_type_callback(void* cookie, const char* /*name*/, const char* value, - uint32_t serial) { - if (0 == strcmp("eng", value) || 0 == strcmp("userdebug", value)) { - reinterpret_cast<StatsService*>(cookie)->mEngBuild = true; - } -} - -/** - * Write data from statsd. - * Format for statsdStats: adb shell dumpsys stats --metadata [-v] [--proto] - * Format for data report: adb shell dumpsys stats [anything other than --metadata] [--proto] - * Anything ending in --proto will be in proto format. - * Anything without --metadata as the first argument will be report information. - * (bugreports call "adb shell dumpsys stats --dump-priority NORMAL -a --proto") - * TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>. - */ -status_t StatsService::dump(int fd, const char** args, uint32_t numArgs) { - if (!checkPermission(kPermissionDump)) { - return PERMISSION_DENIED; - } - - int lastArg = numArgs - 1; - bool asProto = false; - if (lastArg >= 0 && string(args[lastArg]) == "--proto") { // last argument - asProto = true; - lastArg--; - } - if (numArgs > 0 && string(args[0]) == "--metadata") { // first argument - // Request is to dump statsd stats. - bool verbose = false; - if (lastArg >= 0 && string(args[lastArg]) == "-v") { - verbose = true; - lastArg--; - } - dumpStatsdStats(fd, verbose, asProto); - } else { - // Request is to dump statsd report data. - if (asProto) { - dumpIncidentSection(fd); - } else { - dprintf(fd, "Non-proto format of stats data dump not available; see proto version.\n"); - } - } - - return NO_ERROR; -} - -/** - * Write debugging data about statsd in text or proto format. - */ -void StatsService::dumpStatsdStats(int out, bool verbose, bool proto) { - if (proto) { - vector<uint8_t> data; - StatsdStats::getInstance().dumpStats(&data, false); // does not reset statsdStats. - for (size_t i = 0; i < data.size(); i ++) { - dprintf(out, "%c", data[i]); - } - } else { - StatsdStats::getInstance().dumpStats(out); - mProcessor->dumpStates(out, verbose); - } -} - -/** - * Write stats report data in StatsDataDumpProto incident section format. - */ -void StatsService::dumpIncidentSection(int out) { - ProtoOutputStream proto; - for (const ConfigKey& configKey : mConfigManager->GetAllConfigKeys()) { - uint64_t reportsListToken = - proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS_LIST); - // Don't include the current bucket to avoid skipping buckets. - // If we need to include the current bucket later, consider changing to NO_TIME_CONSTRAINTS - // or other alternatives to avoid skipping buckets for pulled metrics. - mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), - false /* includeCurrentBucket */, false /* erase_data */, - ADB_DUMP, - FAST, - &proto); - proto.end(reportsListToken); - proto.flush(out); - proto.clear(); - } -} - -/** - * Implementation of the adb shell cmd stats command. - */ -status_t StatsService::handleShellCommand(int in, int out, int err, const char** argv, - uint32_t argc) { - uid_t uid = AIBinder_getCallingUid(); - if (uid != AID_ROOT && uid != AID_SHELL) { - return PERMISSION_DENIED; - } - - Vector<String8> utf8Args; - utf8Args.setCapacity(argc); - for (uint32_t i = 0; i < argc; i++) { - utf8Args.push(String8(argv[i])); - } - - if (argc >= 1) { - // adb shell cmd stats config ... - if (!utf8Args[0].compare(String8("config"))) { - return cmd_config(in, out, err, utf8Args); - } - - if (!utf8Args[0].compare(String8("print-uid-map"))) { - return cmd_print_uid_map(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("dump-report"))) { - return cmd_dump_report(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("pull-source")) && argc > 1) { - return cmd_print_pulled_metrics(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("send-broadcast"))) { - return cmd_trigger_broadcast(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("print-stats"))) { - return cmd_print_stats(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("meminfo"))) { - return cmd_dump_memory_info(out); - } - - if (!utf8Args[0].compare(String8("write-to-disk"))) { - return cmd_write_data_to_disk(out); - } - - if (!utf8Args[0].compare(String8("log-app-breadcrumb"))) { - return cmd_log_app_breadcrumb(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("log-binary-push"))) { - return cmd_log_binary_push(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("clear-puller-cache"))) { - return cmd_clear_puller_cache(out); - } - - if (!utf8Args[0].compare(String8("print-logs"))) { - return cmd_print_logs(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("send-active-configs"))) { - return cmd_trigger_active_config_broadcast(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("data-subscribe"))) { - { - std::lock_guard<std::mutex> lock(mShellSubscriberMutex); - if (mShellSubscriber == nullptr) { - mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); - } - } - int timeoutSec = -1; - if (argc >= 2) { - timeoutSec = atoi(utf8Args[1].c_str()); - } - mShellSubscriber->startNewSubscription(in, out, timeoutSec); - return NO_ERROR; - } - } - - print_cmd_help(out); - return NO_ERROR; -} - -void StatsService::print_cmd_help(int out) { - dprintf(out, - "usage: adb shell cmd stats print-stats-log [tag_required] " - "[timestamp_nsec_optional]\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats meminfo\n"); - dprintf(out, "\n"); - dprintf(out, " Prints the malloc debug information. You need to run the following first: \n"); - dprintf(out, " # adb shell stop\n"); - dprintf(out, " # adb shell setprop libc.debug.malloc.program statsd \n"); - dprintf(out, " # adb shell setprop libc.debug.malloc.options backtrace \n"); - dprintf(out, " # adb shell start\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats print-uid-map [PKG]\n"); - dprintf(out, "\n"); - dprintf(out, " Prints the UID, app name, version mapping.\n"); - dprintf(out, " PKG Optional package name to print the uids of the package\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats pull-source ATOM_TAG [PACKAGE] \n"); - dprintf(out, "\n"); - dprintf(out, " Prints the output of a pulled atom\n"); - dprintf(out, " UID The atom to pull\n"); - dprintf(out, " PACKAGE The package to pull from. Default is AID_SYSTEM\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats write-to-disk \n"); - dprintf(out, "\n"); - dprintf(out, " Flushes all data on memory to disk.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n"); - dprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n"); - dprintf(out, " UID The uid to use. It is only possible to pass a UID\n"); - dprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); - dprintf(out, " uid is used.\n"); - dprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n"); - dprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, - "usage: adb shell cmd stats log-binary-push NAME VERSION STAGING ROLLBACK_ENABLED " - "LOW_LATENCY STATE EXPERIMENT_IDS\n"); - dprintf(out, " Log a binary push state changed event.\n"); - dprintf(out, " NAME The train name.\n"); - dprintf(out, " VERSION The train version code.\n"); - dprintf(out, " STAGING If this train requires a restart.\n"); - dprintf(out, " ROLLBACK_ENABLED If rollback should be enabled for this install.\n"); - dprintf(out, " LOW_LATENCY If the train requires low latency monitoring.\n"); - dprintf(out, " STATE The status of the train push.\n"); - dprintf(out, " Integer value of the enum in atoms.proto.\n"); - dprintf(out, " EXPERIMENT_IDS Comma separated list of experiment ids.\n"); - dprintf(out, " Leave blank for none.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n"); - dprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n"); - dprintf(out, "\n"); - dprintf(out, " Adds, updates or removes a configuration. The proto should be in\n"); - dprintf(out, " wire-encoded protobuf format and passed via stdin. If no UID and name is\n"); - dprintf(out, " provided, then all configs will be removed from memory and disk.\n"); - dprintf(out, "\n"); - dprintf(out, " UID The uid to use. It is only possible to pass the UID\n"); - dprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); - dprintf(out, " uid is used.\n"); - dprintf(out, " NAME The per-uid name to use\n"); - dprintf(out, "\n"); - dprintf(out, "\n *Note: If both UID and NAME are omitted then all configs will\n"); - dprintf(out, "\n be removed from memory and disk!\n"); - dprintf(out, "\n"); - dprintf(out, - "usage: adb shell cmd stats dump-report [UID] NAME [--keep_data] " - "[--include_current_bucket] [--proto]\n"); - dprintf(out, " Dump all metric data for a configuration.\n"); - dprintf(out, " UID The uid of the configuration. It is only possible to pass\n"); - dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); - dprintf(out, " calling uid is used.\n"); - dprintf(out, " NAME The name of the configuration\n"); - dprintf(out, " --keep_data Do NOT erase the data upon dumping it.\n"); - dprintf(out, " --proto Print proto binary.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n"); - dprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\n"); - dprintf(out, " UID The uid of the configuration. It is only possible to pass\n"); - dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); - dprintf(out, " calling uid is used.\n"); - dprintf(out, " NAME The name of the configuration\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, - "usage: adb shell cmd stats send-active-configs [--uid=UID] [--configs] " - "[NAME1] [NAME2] [NAME3..]\n"); - dprintf(out, " Send a broadcast that informs the subscriber of the current active configs.\n"); - dprintf(out, " --uid=UID The uid of the configurations. It is only possible to pass\n"); - dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); - dprintf(out, " calling uid is used.\n"); - dprintf(out, " --configs Send the list of configs in the name list instead of\n"); - dprintf(out, " the currently active configs\n"); - dprintf(out, " NAME LIST List of configuration names to be included in the broadcast.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats print-stats\n"); - dprintf(out, " Prints some basic stats.\n"); - dprintf(out, " --proto Print proto binary instead of string format.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats clear-puller-cache\n"); - dprintf(out, " Clear cached puller data.\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats print-logs\n"); - dprintf(out, " Requires root privileges.\n"); - dprintf(out, " Can be disabled by calling adb shell cmd stats print-logs 0\n"); -} - -status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) { - string name; - bool good = false; - int uid; - const int argCount = args.size(); - if (argCount == 2) { - // Automatically pick the UID - uid = AIBinder_getCallingUid(); - name.assign(args[1].c_str(), args[1].size()); - good = true; - } else if (argCount == 3) { - good = getUidFromArgs(args, 1, uid); - if (!good) { - dprintf(out, "Invalid UID. Note that the metrics can only be dumped for " - "other UIDs on eng or userdebug builds.\n"); - } - name.assign(args[2].c_str(), args[2].size()); - } - if (!good) { - print_cmd_help(out); - return UNKNOWN_ERROR; - } - ConfigKey key(uid, StrToInt64(name)); - shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key); - if (receiver == nullptr) { - VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str()); - return UNKNOWN_ERROR; - } else if (receiver->sendDataBroadcast(mProcessor->getLastReportTimeNs(key)).isOk()) { - VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(), - args[2].c_str()); - } else { - VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(), args[2].c_str()); - return UNKNOWN_ERROR; - } - return NO_ERROR; -} - -status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector<String8>& args) { - const int argCount = args.size(); - int uid; - vector<int64_t> configIds; - if (argCount == 1) { - // Automatically pick the uid and send a broadcast that has no active configs. - uid = AIBinder_getCallingUid(); - mProcessor->GetActiveConfigs(uid, configIds); - } else { - int curArg = 1; - if(args[curArg].find("--uid=") == 0) { - string uidArgStr(args[curArg].c_str()); - string uidStr = uidArgStr.substr(6); - if (!getUidFromString(uidStr.c_str(), uid)) { - dprintf(out, "Invalid UID. Note that the config can only be set for " - "other UIDs on eng or userdebug builds.\n"); - return UNKNOWN_ERROR; - } - curArg++; - } else { - uid = AIBinder_getCallingUid(); - } - if (curArg == argCount || args[curArg] != "--configs") { - VLOG("Reached end of args, or specify configs not set. Sending actual active configs,"); - mProcessor->GetActiveConfigs(uid, configIds); - } else { - // Flag specified, use the given list of configs. - curArg++; - for (int i = curArg; i < argCount; i++) { - char* endp; - int64_t configID = strtoll(args[i].c_str(), &endp, 10); - if (endp == args[i].c_str() || *endp != '\0') { - dprintf(out, "Error parsing config ID.\n"); - return UNKNOWN_ERROR; - } - VLOG("Adding config id %ld", static_cast<long>(configID)); - configIds.push_back(configID); - } - } - } - shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid); - if (receiver == nullptr) { - VLOG("Could not find receiver for uid %d", uid); - return UNKNOWN_ERROR; - } else if (receiver->sendActiveConfigsChangedBroadcast(configIds).isOk()) { - VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid); - } else { - VLOG("StatsService::trigger active configs changed broadcast failed for uid %d", uid); - return UNKNOWN_ERROR; - } - return NO_ERROR; -} - -status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& args) { - const int argCount = args.size(); - if (argCount >= 2) { - if (args[1] == "update" || args[1] == "remove") { - bool good = false; - int uid = -1; - string name; - - if (argCount == 3) { - // Automatically pick the UID - uid = AIBinder_getCallingUid(); - name.assign(args[2].c_str(), args[2].size()); - good = true; - } else if (argCount == 4) { - good = getUidFromArgs(args, 2, uid); - if (!good) { - dprintf(err, "Invalid UID. Note that the config can only be set for " - "other UIDs on eng or userdebug builds.\n"); - } - name.assign(args[3].c_str(), args[3].size()); - } else if (argCount == 2 && args[1] == "remove") { - good = true; - } - - if (!good) { - // If arg parsing failed, print the help text and return an error. - print_cmd_help(out); - return UNKNOWN_ERROR; - } - - if (args[1] == "update") { - char* endp; - int64_t configID = strtoll(name.c_str(), &endp, 10); - if (endp == name.c_str() || *endp != '\0') { - dprintf(err, "Error parsing config ID.\n"); - return UNKNOWN_ERROR; - } - - // Read stream into buffer. - string buffer; - if (!android::base::ReadFdToString(in, &buffer)) { - dprintf(err, "Error reading stream for StatsConfig.\n"); - return UNKNOWN_ERROR; - } - - // Parse buffer. - StatsdConfig config; - if (!config.ParseFromString(buffer)) { - dprintf(err, "Error parsing proto stream for StatsConfig.\n"); - return UNKNOWN_ERROR; - } - - // Add / update the config. - mConfigManager->UpdateConfig(ConfigKey(uid, configID), config); - } else { - if (argCount == 2) { - cmd_remove_all_configs(out); - } else { - // Remove the config. - mConfigManager->RemoveConfig(ConfigKey(uid, StrToInt64(name))); - } - } - - return NO_ERROR; - } - } - print_cmd_help(out); - return UNKNOWN_ERROR; -} - -status_t StatsService::cmd_dump_report(int out, const Vector<String8>& args) { - if (mProcessor != nullptr) { - int argCount = args.size(); - bool good = false; - bool proto = false; - bool includeCurrentBucket = false; - bool eraseData = true; - int uid; - string name; - if (!std::strcmp("--proto", args[argCount-1].c_str())) { - proto = true; - argCount -= 1; - } - if (!std::strcmp("--include_current_bucket", args[argCount-1].c_str())) { - includeCurrentBucket = true; - argCount -= 1; - } - if (!std::strcmp("--keep_data", args[argCount-1].c_str())) { - eraseData = false; - argCount -= 1; - } - if (argCount == 2) { - // Automatically pick the UID - uid = AIBinder_getCallingUid(); - name.assign(args[1].c_str(), args[1].size()); - good = true; - } else if (argCount == 3) { - good = getUidFromArgs(args, 1, uid); - if (!good) { - dprintf(out, "Invalid UID. Note that the metrics can only be dumped for " - "other UIDs on eng or userdebug builds.\n"); - } - name.assign(args[2].c_str(), args[2].size()); - } - if (good) { - vector<uint8_t> data; - mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(), - includeCurrentBucket, eraseData, ADB_DUMP, - NO_TIME_CONSTRAINTS, - &data); - if (proto) { - for (size_t i = 0; i < data.size(); i ++) { - dprintf(out, "%c", data[i]); - } - } else { - dprintf(out, "Non-proto stats data dump not currently supported.\n"); - } - return android::OK; - } else { - // If arg parsing failed, print the help text and return an error. - print_cmd_help(out); - return UNKNOWN_ERROR; - } - } else { - dprintf(out, "Log processor does not exist...\n"); - return UNKNOWN_ERROR; - } -} - -status_t StatsService::cmd_print_stats(int out, const Vector<String8>& args) { - int argCount = args.size(); - bool proto = false; - if (!std::strcmp("--proto", args[argCount-1].c_str())) { - proto = true; - argCount -= 1; - } - StatsdStats& statsdStats = StatsdStats::getInstance(); - if (proto) { - vector<uint8_t> data; - statsdStats.dumpStats(&data, false); // does not reset statsdStats. - for (size_t i = 0; i < data.size(); i ++) { - dprintf(out, "%c", data[i]); - } - - } else { - vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys(); - for (const ConfigKey& key : configs) { - dprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(), - mProcessor->GetMetricsSize(key)); - } - statsdStats.dumpStats(out); - } - return NO_ERROR; -} - -status_t StatsService::cmd_print_uid_map(int out, const Vector<String8>& args) { - if (args.size() > 1) { - string pkg; - pkg.assign(args[1].c_str(), args[1].size()); - auto uids = mUidMap->getAppUid(pkg); - dprintf(out, "%s -> [ ", pkg.c_str()); - for (const auto& uid : uids) { - dprintf(out, "%d ", uid); - } - dprintf(out, "]\n"); - } else { - mUidMap->printUidMap(out); - } - return NO_ERROR; -} - -status_t StatsService::cmd_write_data_to_disk(int out) { - dprintf(out, "Writing data to disk\n"); - mProcessor->WriteDataToDisk(ADB_DUMP, NO_TIME_CONSTRAINTS); - return NO_ERROR; -} - -status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& args) { - bool good = false; - int32_t uid; - int32_t label; - int32_t state; - const int argCount = args.size(); - if (argCount == 3) { - // Automatically pick the UID - uid = AIBinder_getCallingUid(); - label = atoi(args[1].c_str()); - state = atoi(args[2].c_str()); - good = true; - } else if (argCount == 4) { - good = getUidFromArgs(args, 1, uid); - if (!good) { - dprintf(out, - "Invalid UID. Note that selecting a UID for writing AppBreadcrumb can only be " - "done for other UIDs on eng or userdebug builds.\n"); - } - label = atoi(args[2].c_str()); - state = atoi(args[3].c_str()); - } - if (good) { - dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", 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; - } - return NO_ERROR; -} - -status_t StatsService::cmd_log_binary_push(int out, const Vector<String8>& args) { - // Security checks are done in the sendBinaryPushStateChanged atom. - const int argCount = args.size(); - if (argCount != 7 && argCount != 8) { - dprintf(out, "Incorrect number of argument supplied\n"); - return UNKNOWN_ERROR; - } - string trainName = string(args[1].c_str()); - int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10); - int32_t state = atoi(args[6].c_str()); - vector<int64_t> experimentIds; - if (argCount == 8) { - vector<string> experimentIdsString = android::base::Split(string(args[7].c_str()), ","); - for (string experimentIdString : experimentIdsString) { - int64_t experimentId = strtoll(experimentIdString.c_str(), nullptr, 10); - experimentIds.push_back(experimentId); - } - } - dprintf(out, "Logging BinaryPushStateChanged\n"); - vector<uint8_t> experimentIdBytes; - writeExperimentIdsToProto(experimentIds, &experimentIdBytes); - LogEvent event(trainName, trainVersion, args[3], args[4], args[5], state, experimentIdBytes, 0); - mProcessor->OnLogEvent(&event); - return NO_ERROR; -} - -status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) { - int s = atoi(args[1].c_str()); - vector<int32_t> uids; - if (args.size() > 2) { - string package = string(args[2].c_str()); - auto it = UidMap::sAidToUidMapping.find(package); - if (it != UidMap::sAidToUidMapping.end()) { - uids.push_back(it->second); - } else { - set<int32_t> uids_set = mUidMap->getAppUid(package); - uids.insert(uids.end(), uids_set.begin(), uids_set.end()); - } - } else { - uids.push_back(AID_SYSTEM); - } - vector<shared_ptr<LogEvent>> stats; - if (mPullerManager->Pull(s, uids, getElapsedRealtimeNs(), &stats)) { - for (const auto& it : stats) { - dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str()); - } - dprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size()); - return NO_ERROR; - } - return UNKNOWN_ERROR; -} - -status_t StatsService::cmd_remove_all_configs(int out) { - dprintf(out, "Removing all configs...\n"); - VLOG("StatsService::cmd_remove_all_configs was called"); - mConfigManager->RemoveAllConfigs(); - StorageManager::deleteAllFiles(STATS_SERVICE_DIR); - return NO_ERROR; -} - -status_t StatsService::cmd_dump_memory_info(int out) { - dprintf(out, "meminfo not available.\n"); - return NO_ERROR; -} - -status_t StatsService::cmd_clear_puller_cache(int out) { - VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i", - AIBinder_getCallingPid(), AIBinder_getCallingUid()); - if (checkPermission(kPermissionDump)) { - int cleared = mPullerManager->ForceClearPullerCache(); - dprintf(out, "Puller removed %d cached data!\n", cleared); - return NO_ERROR; - } else { - return PERMISSION_DENIED; - } -} - -status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) { - Status status = checkUid(AID_ROOT); - if (!status.isOk()) { - return PERMISSION_DENIED; - } - - VLOG("StatsService::cmd_print_logs with pid %i, uid %i", AIBinder_getCallingPid(), - AIBinder_getCallingUid()); - bool enabled = true; - if (args.size() >= 2) { - enabled = atoi(args[1].c_str()) != 0; - } - mProcessor->setPrintLogs(enabled); - return NO_ERROR; -} - -bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) { - return getUidFromString(args[uidArgIndex].c_str(), uid); -} - -bool StatsService::getUidFromString(const char* s, int32_t& uid) { - if (*s == '\0') { - return false; - } - char* endc = NULL; - int64_t longUid = strtol(s, &endc, 0); - if (*endc != '\0') { - return false; - } - int32_t goodUid = static_cast<int32_t>(longUid); - if (longUid < 0 || static_cast<uint64_t>(longUid) != static_cast<uid_t>(goodUid)) { - return false; // It was not of uid_t type. - } - uid = goodUid; - - int32_t callingUid = AIBinder_getCallingUid(); - return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids. - || (callingUid == goodUid) // Anyone can 'impersonate' themselves. - || (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL. -} - -Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) { - ENFORCE_UID(AID_SYSTEM); - // Read stream into buffer. - string buffer; - if (!android::base::ReadFdToString(fd.get(), &buffer)) { - return exception(EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe."); - } - - // Parse buffer. - UidData uidData; - if (!uidData.ParseFromString(buffer)) { - return exception(EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData."); - } - - vector<String16> versionStrings; - vector<String16> installers; - vector<String16> packageNames; - vector<int32_t> uids; - vector<int64_t> versions; - - const auto numEntries = uidData.app_info_size(); - versionStrings.reserve(numEntries); - installers.reserve(numEntries); - packageNames.reserve(numEntries); - uids.reserve(numEntries); - versions.reserve(numEntries); - - for (const auto& appInfo: uidData.app_info()) { - packageNames.emplace_back(String16(appInfo.package_name().c_str())); - uids.push_back(appInfo.uid()); - versions.push_back(appInfo.version()); - versionStrings.emplace_back(String16(appInfo.version_string().c_str())); - installers.emplace_back(String16(appInfo.installer().c_str())); - } - - mUidMap->updateMap(getElapsedRealtimeNs(), - uids, - versions, - versionStrings, - packageNames, - installers); - - mBootCompleteTrigger.markComplete(kUidMapReceivedTag); - VLOG("StatsService::informAllUidData UidData proto parsed successfully."); - return Status::ok(); -} - -Status StatsService::informOnePackage(const string& app, int32_t uid, int64_t version, - const string& versionString, const string& installer) { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::informOnePackage was called"); - String16 utf16App = String16(app.c_str()); - String16 utf16VersionString = String16(versionString.c_str()); - String16 utf16Installer = String16(installer.c_str()); - - mUidMap->updateApp(getElapsedRealtimeNs(), utf16App, uid, version, utf16VersionString, - utf16Installer); - return Status::ok(); -} - -Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::informOnePackageRemoved was called"); - String16 utf16App = String16(app.c_str()); - mUidMap->removeApp(getElapsedRealtimeNs(), utf16App, uid); - mConfigManager->RemoveConfigs(uid); - return Status::ok(); -} - -Status StatsService::informAlarmForSubscriberTriggeringFired() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called"); - int64_t currentTimeSec = getElapsedRealtimeSec(); - std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet = - mPeriodicAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); - if (alarmSet.size() > 0) { - VLOG("Found periodic alarm fired."); - mProcessor->onPeriodicAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet); - } else { - ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled."); - } - return Status::ok(); -} - -Status StatsService::informPollAlarmFired() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::informPollAlarmFired was called"); - mProcessor->informPullAlarmFired(getElapsedRealtimeNs()); - VLOG("StatsService::informPollAlarmFired succeeded"); - return Status::ok(); -} - -Status StatsService::systemRunning() { - ENFORCE_UID(AID_SYSTEM); - - // When system_server is up and running, schedule the dropbox task to run. - VLOG("StatsService::systemRunning"); - sayHiToStatsCompanion(); - return Status::ok(); -} - -Status StatsService::informDeviceShutdown() { - ENFORCE_UID(AID_SYSTEM); - VLOG("StatsService::informDeviceShutdown"); - mProcessor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST); - mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs()); - mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs()); - return Status::ok(); -} - -void StatsService::sayHiToStatsCompanion() { - shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService(); - if (statsCompanion != nullptr) { - VLOG("Telling statsCompanion that statsd is ready"); - statsCompanion->statsdReady(); - } else { - VLOG("Could not access statsCompanion"); - } -} - -Status StatsService::statsCompanionReady() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::statsCompanionReady was called"); - shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService(); - if (statsCompanion == nullptr) { - return exception(EX_NULL_POINTER, - "StatsCompanion unavailable despite it contacting statsd."); - } - VLOG("StatsService::statsCompanionReady linking to statsCompanion."); - AIBinder_linkToDeath(statsCompanion->asBinder().get(), - mStatsCompanionServiceDeathRecipient.get(), this); - mPullerManager->SetStatsCompanionService(statsCompanion); - mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion); - mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion); - return Status::ok(); -} - -Status StatsService::bootCompleted() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::bootCompleted was called"); - mBootCompleteTrigger.markComplete(kBootCompleteTag); - return Status::ok(); -} - -void StatsService::Startup() { - mConfigManager->Startup(); - mProcessor->LoadActiveConfigsFromDisk(); - mProcessor->LoadMetadataFromDisk(getWallClockNs(), getElapsedRealtimeNs()); -} - -void StatsService::Terminate() { - ALOGI("StatsService::Terminating"); - if (mProcessor != nullptr) { - mProcessor->WriteDataToDisk(TERMINATION_SIGNAL_RECEIVED, FAST); - mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs()); - mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs()); - } -} - -// Test only interface!!! -void StatsService::OnLogEvent(LogEvent* event) { - mProcessor->OnLogEvent(event); - if (mShellSubscriber != nullptr) { - mShellSubscriber->onLogEvent(*event); - } -} - -Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::getData with Uid %i", callingUid); - ConfigKey configKey(callingUid, key); - // The dump latency does not matter here since we do not include the current bucket, we do not - // need to pull any new data anyhow. - mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/, - true /* erase_data */, GET_DATA_CALLED, FAST, output); - return Status::ok(); -} - -Status StatsService::getMetadata(vector<uint8_t>* output) { - ENFORCE_UID(AID_SYSTEM); - - StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters. - return Status::ok(); -} - -Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config, - const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - if (addConfigurationChecked(callingUid, key, config)) { - return Status::ok(); - } else { - return exception(EX_ILLEGAL_ARGUMENT, "Could not parse malformatted StatsdConfig."); - } -} - -bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config) { - ConfigKey configKey(uid, key); - StatsdConfig cfg; - if (config.size() > 0) { // If the config is empty, skip parsing. - if (!cfg.ParseFromArray(&config[0], config.size())) { - return false; - } - } - mConfigManager->UpdateConfig(configKey, cfg); - return true; -} - -Status StatsService::removeDataFetchOperation(int64_t key, - const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - ConfigKey configKey(callingUid, key); - mConfigManager->RemoveConfigReceiver(configKey); - return Status::ok(); -} - -Status StatsService::setDataFetchOperation(int64_t key, - const shared_ptr<IPendingIntentRef>& pir, - const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - ConfigKey configKey(callingUid, key); - mConfigManager->SetConfigReceiver(configKey, pir); - if (StorageManager::hasConfigMetricsReport(configKey)) { - VLOG("StatsService::setDataFetchOperation marking configKey %s to dump reports on disk", - configKey.ToString().c_str()); - mProcessor->noteOnDiskData(configKey); - } - return Status::ok(); -} - -Status StatsService::setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir, - const int32_t callingUid, - vector<int64_t>* output) { - ENFORCE_UID(AID_SYSTEM); - - mConfigManager->SetActiveConfigsChangedReceiver(callingUid, pir); - if (output != nullptr) { - mProcessor->GetActiveConfigs(callingUid, *output); - } else { - ALOGW("StatsService::setActiveConfigsChanged output was nullptr"); - } - return Status::ok(); -} - -Status StatsService::removeActiveConfigsChangedOperation(const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - mConfigManager->RemoveActiveConfigsChangedReceiver(callingUid); - return Status::ok(); -} - -Status StatsService::removeConfiguration(int64_t key, const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - ConfigKey configKey(callingUid, key); - mConfigManager->RemoveConfig(configKey); - return Status::ok(); -} - -Status StatsService::setBroadcastSubscriber(int64_t configId, - int64_t subscriberId, - const shared_ptr<IPendingIntentRef>& pir, - const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::setBroadcastSubscriber called."); - ConfigKey configKey(callingUid, configId); - SubscriberReporter::getInstance() - .setBroadcastSubscriber(configKey, subscriberId, pir); - return Status::ok(); -} - -Status StatsService::unsetBroadcastSubscriber(int64_t configId, - int64_t subscriberId, - const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::unsetBroadcastSubscriber called."); - ConfigKey configKey(callingUid, configId); - SubscriberReporter::getInstance() - .unsetBroadcastSubscriber(configKey, subscriberId); - return Status::ok(); -} - -Status StatsService::allPullersFromBootRegistered() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::allPullersFromBootRegistered was called"); - mBootCompleteTrigger.markComplete(kAllPullersRegisteredTag); - return Status::ok(); -} - -Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownMillis, - int64_t timeoutMillis, - const std::vector<int32_t>& additiveFields, - const shared_ptr<IPullAtomCallback>& pullerCallback) { - ENFORCE_UID(AID_SYSTEM); - VLOG("StatsService::registerPullAtomCallback called."); - mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis), - MillisToNano(timeoutMillis), additiveFields, - pullerCallback); - return Status::ok(); -} - -Status StatsService::registerNativePullAtomCallback( - int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis, - const std::vector<int32_t>& additiveFields, - const shared_ptr<IPullAtomCallback>& pullerCallback) { - if (!checkPermission(kPermissionRegisterPullAtom)) { - return exception( - EX_SECURITY, - StringPrintf("Uid %d does not have the %s permission when registering atom %d", - AIBinder_getCallingUid(), kPermissionRegisterPullAtom, atomTag)); - } - VLOG("StatsService::registerNativePullAtomCallback called."); - int32_t uid = AIBinder_getCallingUid(); - mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis), - MillisToNano(timeoutMillis), additiveFields, - pullerCallback); - return Status::ok(); -} - -Status StatsService::unregisterPullAtomCallback(int32_t uid, int32_t atomTag) { - ENFORCE_UID(AID_SYSTEM); - VLOG("StatsService::unregisterPullAtomCallback called."); - mPullerManager->UnregisterPullAtomCallback(uid, atomTag); - return Status::ok(); -} - -Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) { - if (!checkPermission(kPermissionRegisterPullAtom)) { - return exception( - EX_SECURITY, - StringPrintf("Uid %d does not have the %s permission when unregistering atom %d", - AIBinder_getCallingUid(), kPermissionRegisterPullAtom, atomTag)); - } - VLOG("StatsService::unregisterNativePullAtomCallback called."); - int32_t uid = AIBinder_getCallingUid(); - mPullerManager->UnregisterPullAtomCallback(uid, atomTag); - return Status::ok(); -} - -Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) { - ENFORCE_UID(AID_SYSTEM); - // TODO: add verifier permission - - experimentIdsOut->clear(); - // Read the latest train info - vector<InstallTrainInfo> trainInfoList = StorageManager::readAllTrainInfo(); - if (trainInfoList.empty()) { - // No train info means no experiment IDs, return an empty list - return Status::ok(); - } - - // Copy the experiment IDs to the out vector - for (InstallTrainInfo& trainInfo : trainInfoList) { - experimentIdsOut->insert(experimentIdsOut->end(), - trainInfo.experimentIds.begin(), - trainInfo.experimentIds.end()); - } - return Status::ok(); -} - -void StatsService::statsCompanionServiceDied(void* cookie) { - auto thiz = static_cast<StatsService*>(cookie); - thiz->statsCompanionServiceDiedImpl(); -} - -void StatsService::statsCompanionServiceDiedImpl() { - ALOGW("statscompanion service died"); - StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec()); - if (mProcessor != nullptr) { - ALOGW("Reset statsd upon system server restarts."); - int64_t systemServerRestartNs = getElapsedRealtimeNs(); - ProtoOutputStream activeConfigsProto; - mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs, - STATSCOMPANION_DIED, &activeConfigsProto); - metadata::StatsMetadataList metadataList; - mProcessor->WriteMetadataToProto(getWallClockNs(), - systemServerRestartNs, &metadataList); - mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST); - mProcessor->resetConfigs(); - - std::string serializedActiveConfigs; - if (activeConfigsProto.serializeToString(&serializedActiveConfigs)) { - ActiveConfigList activeConfigs; - if (activeConfigs.ParseFromString(serializedActiveConfigs)) { - mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs); - } - } - mProcessor->SetMetadataState(metadataList, getWallClockNs(), systemServerRestartNs); - } - mAnomalyAlarmMonitor->setStatsCompanionService(nullptr); - mPeriodicAlarmMonitor->setStatsCompanionService(nullptr); - mPullerManager->SetStatsCompanionService(nullptr); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h deleted file mode 100644 index 479f4e87ec96..000000000000 --- a/cmds/statsd/src/StatsService.h +++ /dev/null @@ -1,416 +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. - */ - -#ifndef STATS_SERVICE_H -#define STATS_SERVICE_H - -#include <aidl/android/os/BnStatsd.h> -#include <aidl/android/os/IPendingIntentRef.h> -#include <aidl/android/os/IPullAtomCallback.h> -#include <gtest/gtest_prod.h> -#include <utils/Looper.h> - -#include <mutex> - -#include "StatsLogProcessor.h" -#include "anomaly/AlarmMonitor.h" -#include "config/ConfigManager.h" -#include "external/StatsPullerManager.h" -#include "logd/LogEventQueue.h" -#include "packages/UidMap.h" -#include "shell/ShellSubscriber.h" -#include "statscompanion_util.h" -#include "utils/MultiConditionTrigger.h" - -using namespace android; -using namespace android::os; -using namespace std; - -using Status = ::ndk::ScopedAStatus; -using aidl::android::os::BnStatsd; -using aidl::android::os::IPendingIntentRef; -using aidl::android::os::IPullAtomCallback; -using ::ndk::ScopedAIBinder_DeathRecipient; -using ::ndk::ScopedFileDescriptor; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -class StatsService : public BnStatsd { -public: - StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue); - virtual ~StatsService(); - - /** The anomaly alarm registered with AlarmManager won't be updated by less than this. */ - const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5; - - virtual status_t dump(int fd, const char** args, uint32_t numArgs) override; - virtual status_t handleShellCommand(int in, int out, int err, const char** argv, - uint32_t argc) override; - - virtual Status systemRunning(); - virtual Status statsCompanionReady(); - virtual Status bootCompleted(); - virtual Status informPollAlarmFired(); - virtual Status informAlarmForSubscriberTriggeringFired(); - - virtual Status informAllUidData(const ScopedFileDescriptor& fd); - virtual Status informOnePackage(const string& app, int32_t uid, int64_t version, - const string& versionString, const string& installer); - virtual Status informOnePackageRemoved(const string& app, int32_t uid); - virtual Status informDeviceShutdown(); - - /** - * Called right before we start processing events. - */ - void Startup(); - - /** - * Called when terminiation signal received. - */ - void Terminate(); - - /** - * Test ONLY interface. In real world, StatsService reads from LogEventQueue. - */ - virtual void OnLogEvent(LogEvent* event); - - /** - * Binder call for clients to request data for this configuration key. - */ - virtual Status getData(int64_t key, - const int32_t callingUid, - vector<uint8_t>* output) override; - - - /** - * Binder call for clients to get metadata across all configs in statsd. - */ - virtual Status getMetadata(vector<uint8_t>* output) override; - - - /** - * Binder call to let clients send a configuration and indicate they're interested when they - * should requestData for this configuration. - */ - virtual Status addConfiguration(int64_t key, - const vector<uint8_t>& config, - const int32_t callingUid) override; - - /** - * Binder call to let clients register the data fetch operation for a configuration. - */ - virtual Status setDataFetchOperation(int64_t key, - const shared_ptr<IPendingIntentRef>& pir, - const int32_t callingUid) override; - - /** - * Binder call to remove the data fetch operation for the specified config key. - */ - virtual Status removeDataFetchOperation(int64_t key, - const int32_t callingUid) override; - - /** - * Binder call to let clients register the active configs changed operation. - */ - virtual Status setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir, - const int32_t callingUid, - vector<int64_t>* output) override; - - /** - * Binder call to remove the active configs changed operation for the specified package.. - */ - virtual Status removeActiveConfigsChangedOperation(const int32_t callingUid) override; - /** - * Binder call to allow clients to remove the specified configuration. - */ - virtual Status removeConfiguration(int64_t key, - const int32_t callingUid) override; - - /** - * Binder call to associate the given config's subscriberId with the given pendingIntentRef. - */ - virtual Status setBroadcastSubscriber(int64_t configId, - int64_t subscriberId, - const shared_ptr<IPendingIntentRef>& pir, - const int32_t callingUid) override; - - /** - * Binder call to unassociate the given config's subscriberId with any pendingIntentRef. - */ - virtual Status unsetBroadcastSubscriber(int64_t configId, - int64_t subscriberId, - const int32_t callingUid) override; - - /** Inform statsCompanion that statsd is ready. */ - virtual void sayHiToStatsCompanion(); - - /** - * Binder call to notify statsd that all pullers from boot have been registered. - */ - virtual Status allPullersFromBootRegistered(); - - /** - * Binder call to register a callback function for a pulled atom. - */ - virtual Status registerPullAtomCallback( - int32_t uid, int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis, - const std::vector<int32_t>& additiveFields, - const shared_ptr<IPullAtomCallback>& pullerCallback) override; - - /** - * Binder call to register a callback function for a pulled atom. - */ - virtual Status registerNativePullAtomCallback( - int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis, - const std::vector<int32_t>& additiveFields, - const shared_ptr<IPullAtomCallback>& pullerCallback) override; - - /** - * Binder call to unregister any existing callback for the given uid and atom. - */ - virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override; - - /** - * Binder call to unregister any existing callback for the given atom and calling uid. - */ - virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override; - - /** - * Binder call to get registered experiment IDs. - */ - virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut); - -private: - /** - * Load system properties at init. - */ - void init_system_properties(); - - /** - * Helper for loading system properties. - */ - static void init_build_type_callback(void* cookie, const char* name, const char* value, - uint32_t serial); - - /** - * Proto output of statsd report data dumpsys, wrapped in a StatsDataDumpProto. - */ - void dumpIncidentSection(int outFd); - - /** - * Text or proto output of statsdStats dumpsys. - */ - void dumpStatsdStats(int outFd, bool verbose, bool proto); - - /** - * Print usage information for the commands - */ - void print_cmd_help(int out); - - /* Runs on its dedicated thread to process pushed stats event from socket. */ - void readLogs(); - - /** - * Trigger a broadcast. - */ - status_t cmd_trigger_broadcast(int outFd, Vector<String8>& args); - - - /** - * Trigger an active configs changed broadcast. - */ - status_t cmd_trigger_active_config_broadcast(int outFd, Vector<String8>& args); - - /** - * Handle the config sub-command. - */ - status_t cmd_config(int inFd, int outFd, int err, Vector<String8>& args); - - /** - * Prints some basic stats to std out. - */ - status_t cmd_print_stats(int outFd, const Vector<String8>& args); - - /** - * Print the event log. - */ - status_t cmd_dump_report(int outFd, const Vector<String8>& args); - - /** - * Print the mapping of uids to package names. - */ - status_t cmd_print_uid_map(int outFd, const Vector<String8>& args); - - /** - * Flush the data to disk. - */ - status_t cmd_write_data_to_disk(int outFd); - - /** - * Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling - * StatsLog.write(APP_BREADCRUMB_REPORTED). - */ - status_t cmd_log_app_breadcrumb(int outFd, const Vector<String8>& args); - - /** - * Write an BinaryPushStateChanged event, as if calling StatsLog.logBinaryPushStateChanged(). - */ - status_t cmd_log_binary_push(int outFd, const Vector<String8>& args); - - /** - * Print contents of a pulled metrics source. - */ - status_t cmd_print_pulled_metrics(int outFd, const Vector<String8>& args); - - /** - * Removes all configs stored on disk and on memory. - */ - status_t cmd_remove_all_configs(int outFd); - - /* - * Dump memory usage by statsd. - */ - status_t cmd_dump_memory_info(int outFd); - - /* - * Clear all puller cached data - */ - status_t cmd_clear_puller_cache(int outFd); - - /** - * Print all stats logs received to logcat. - */ - status_t cmd_print_logs(int outFd, const Vector<String8>& args); - - /** - * Writes the value of args[uidArgIndex] into uid. - * Returns whether the uid is reasonable (type uid_t) and whether - * 1. it is equal to the calling uid, or - * 2. the device is mEngBuild, or - * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL). - */ - bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid); - - /** - * Writes the value of uidSting into uid. - * Returns whether the uid is reasonable (type uid_t) and whether - * 1. it is equal to the calling uid, or - * 2. the device is mEngBuild, or - * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL). - */ - bool getUidFromString(const char* uidString, int32_t& uid); - - /** - * Adds a configuration after checking permissions and obtaining UID from binder call. - */ - bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config); - - /** - * Update a configuration. - */ - void set_config(int uid, const string& name, const StatsdConfig& config); - - /** - * Death recipient callback that is called when StatsCompanionService dies. - * The cookie is a pointer to a StatsService object. - */ - static void statsCompanionServiceDied(void* cookie); - - /** - * Implementation of statsCompanionServiceDied. - */ - void statsCompanionServiceDiedImpl(); - - /** - * Tracks the uid <--> package name mapping. - */ - sp<UidMap> mUidMap; - - /** - * Fetches external metrics - */ - sp<StatsPullerManager> mPullerManager; - - /** - * Tracks the configurations that have been passed to statsd. - */ - sp<ConfigManager> mConfigManager; - - /** - * The metrics recorder. - */ - sp<StatsLogProcessor> mProcessor; - - /** - * The alarm monitor for anomaly detection. - */ - const sp<AlarmMonitor> mAnomalyAlarmMonitor; - - /** - * The alarm monitor for alarms to directly trigger subscriber. - */ - const sp<AlarmMonitor> mPeriodicAlarmMonitor; - - /** - * Whether this is an eng build. - */ - bool mEngBuild; - - sp<ShellSubscriber> mShellSubscriber; - - /** - * Mutex for setting the shell subscriber - */ - mutable mutex mShellSubscriberMutex; - std::shared_ptr<LogEventQueue> mEventQueue; - - MultiConditionTrigger mBootCompleteTrigger; - static const inline string kBootCompleteTag = "BOOT_COMPLETE"; - static const inline string kUidMapReceivedTag = "UID_MAP"; - static const inline string kAllPullersRegisteredTag = "PULLERS_REGISTERED"; - - ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient; - - FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); - FRIEND_TEST(StatsServiceTest, TestAddConfig_simple); - FRIEND_TEST(StatsServiceTest, TestAddConfig_empty); - FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid); - FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs); - FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp); - FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot); - FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade); - FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval); - FRIEND_TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit); - FRIEND_TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket); - FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket); - FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket); - FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket); - FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket); - FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket); - - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // STATS_SERVICE_H diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto deleted file mode 100644 index 992983358ae6..000000000000 --- a/cmds/statsd/src/active_config_list.proto +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.os.statsd; -option java_package = "com.android.os"; -option java_multiple_files = true; -option java_outer_classname = "ActiveConfigProto"; - -message ActiveEventActivation { - optional int32 atom_matcher_index = 1; - - // Time left in activation. When this proto is loaded after device boot, - // the activation should be set to active for this duration. - // This field will only be set when the state is ACTIVE - optional int64 remaining_ttl_nanos = 2; - - enum State { - UNNKNOWN = 0; - // This metric should activate for remaining_ttl_nanos when we load the activations. - ACTIVE = 1; - // When we load the activations, this metric should activate on next boot for the tll - // specified in the config. - ACTIVATE_ON_BOOT = 2; - } - optional State state = 3; -} - -message ActiveMetric { - optional int64 id = 1; - repeated ActiveEventActivation activation = 2; -} - -message ActiveConfig { - optional int64 id = 1; - optional int32 uid = 2; - repeated ActiveMetric metric = 3; -} - -// all configs and their metrics on device. -message ActiveConfigList { - repeated ActiveConfig config = 1; -} diff --git a/cmds/statsd/src/annotations.h b/cmds/statsd/src/annotations.h deleted file mode 100644 index cf7f5433663f..000000000000 --- a/cmds/statsd/src/annotations.h +++ /dev/null @@ -1,33 +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. - */ - -#pragma once - -namespace android { -namespace os { -namespace statsd { - -const uint8_t ANNOTATION_ID_IS_UID = 1; -const uint8_t ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2; -const uint8_t ANNOTATION_ID_PRIMARY_FIELD = 3; -const uint8_t ANNOTATION_ID_EXCLUSIVE_STATE = 4; -const uint8_t ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5; -const uint8_t ANNOTATION_ID_TRIGGER_STATE_RESET = 7; -const uint8_t ANNOTATION_ID_STATE_NESTED = 8; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp deleted file mode 100644 index b632d040eb43..000000000000 --- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp +++ /dev/null @@ -1,139 +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. - */ - -#define DEBUG false -#include "Log.h" - -#include "anomaly/AlarmMonitor.h" -#include "guardrail/StatsdStats.h" - -namespace android { -namespace os { -namespace statsd { - -AlarmMonitor::AlarmMonitor( - uint32_t minDiffToUpdateRegisteredAlarmTimeSec, - const std::function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>& updateAlarm, - const std::function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm) - : mRegisteredAlarmTimeSec(0), - mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec), - mUpdateAlarm(updateAlarm), - mCancelAlarm(cancelAlarm) {} - -AlarmMonitor::~AlarmMonitor() {} - -void AlarmMonitor::setStatsCompanionService( - shared_ptr<IStatsCompanionService> statsCompanionService) { - std::lock_guard<std::mutex> lock(mLock); - shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService; - mStatsCompanionService = statsCompanionService; - if (statsCompanionService == nullptr) { - VLOG("Erasing link to statsCompanionService"); - return; - } - VLOG("Creating link to statsCompanionService"); - const sp<const InternalAlarm> top = mPq.top(); - if (top != nullptr) { - updateRegisteredAlarmTime_l(top->timestampSec); - } -} - -void AlarmMonitor::add(sp<const InternalAlarm> alarm) { - std::lock_guard<std::mutex> lock(mLock); - if (alarm == nullptr) { - ALOGW("Asked to add a null alarm."); - return; - } - if (alarm->timestampSec < 1) { - // forbidden since a timestamp 0 is used to indicate no alarm registered - ALOGW("Asked to add a 0-time alarm."); - return; - } - // TODO(b/110563466): Ensure that refractory period is respected. - VLOG("Adding alarm with time %u", alarm->timestampSec); - mPq.push(alarm); - if (mRegisteredAlarmTimeSec < 1 || - alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) { - updateRegisteredAlarmTime_l(alarm->timestampSec); - } -} - -void AlarmMonitor::remove(sp<const InternalAlarm> alarm) { - std::lock_guard<std::mutex> lock(mLock); - if (alarm == nullptr) { - ALOGW("Asked to remove a null alarm."); - return; - } - VLOG("Removing alarm with time %u", alarm->timestampSec); - bool wasPresent = mPq.remove(alarm); - if (!wasPresent) return; - if (mPq.empty()) { - VLOG("Queue is empty. Cancel any alarm."); - cancelRegisteredAlarmTime_l(); - return; - } - uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec; - VLOG("Soonest alarm is %u", soonestAlarmTimeSec); - if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) { - updateRegisteredAlarmTime_l(soonestAlarmTimeSec); - } -} - -// More efficient than repeatedly calling remove(mPq.top()) since it batches the -// updates to the registered alarm. -unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> AlarmMonitor::popSoonerThan( - uint32_t timestampSec) { - VLOG("Removing alarms with time <= %u", timestampSec); - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> oldAlarms; - std::lock_guard<std::mutex> lock(mLock); - - for (sp<const InternalAlarm> t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec; - t = mPq.top()) { - oldAlarms.insert(t); - mPq.pop(); // remove t - } - // Always update registered alarm time (if anything has changed). - if (!oldAlarms.empty()) { - if (mPq.empty()) { - VLOG("Queue is empty. Cancel any alarm."); - cancelRegisteredAlarmTime_l(); - } else { - // Always update the registered alarm in this case (unlike remove()). - updateRegisteredAlarmTime_l(mPq.top()->timestampSec); - } - } - return oldAlarms; -} - -void AlarmMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) { - VLOG("Updating reg alarm time to %u", timestampSec); - mRegisteredAlarmTimeSec = timestampSec; - mUpdateAlarm(mStatsCompanionService, secToMs(mRegisteredAlarmTimeSec)); -} - -void AlarmMonitor::cancelRegisteredAlarmTime_l() { - VLOG("Cancelling reg alarm."); - mRegisteredAlarmTimeSec = 0; - mCancelAlarm(mStatsCompanionService); -} - -int64_t AlarmMonitor::secToMs(uint32_t timeSec) { - return ((int64_t)timeSec) * 1000; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h deleted file mode 100644 index 5c34e381ba0b..000000000000 --- a/cmds/statsd/src/anomaly/AlarmMonitor.h +++ /dev/null @@ -1,162 +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. - */ - -#pragma once - -#include "anomaly/indexed_priority_queue.h" - -#include <aidl/android/os/IStatsCompanionService.h> -#include <utils/RefBase.h> - -#include <unordered_set> -#include <vector> - -using namespace android; - -using aidl::android::os::IStatsCompanionService; -using std::function; -using std::shared_ptr; -using std::unordered_set; - -namespace android { -namespace os { -namespace statsd { - -/** - * Represents an alarm, associated with some aggregate metric, holding a - * projected time at which the metric is expected to exceed its anomaly - * threshold. - * Timestamps are in seconds since epoch in a uint32, so will fail in year 2106. - */ -struct InternalAlarm : public RefBase { - explicit InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) { - } - - const uint32_t timestampSec; - - /** InternalAlarm a is smaller (higher priority) than b if its timestamp is sooner. */ - struct SmallerTimestamp { - bool operator()(sp<const InternalAlarm> a, sp<const InternalAlarm> b) const { - return (a->timestampSec < b->timestampSec); - } - }; -}; - -/** - * Manages internal alarms that may get registered with the AlarmManager. - */ -class AlarmMonitor : public RefBase { -public: - /** - * @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs - * from the registered alarm by more than this amount, update the registered - * alarm. - */ - AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec, - const function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>& - updateAlarm, - const function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm); - ~AlarmMonitor(); - - /** - * Tells AnomalyMonitor what IStatsCompanionService to use and, if - * applicable, immediately registers an existing alarm with it. - * If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't - * update IStatsCompanionService (until such time as it is set non-null). - */ - void setStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService); - - /** - * Adds the given alarm (reference) to the queue. - */ - void add(sp<const InternalAlarm> alarm); - - /** - * Removes the given alarm (reference) from the queue. - * Note that alarm comparison is reference-based; if another alarm exists - * with the same timestampSec, that alarm will still remain in the queue. - */ - void remove(sp<const InternalAlarm> alarm); - - /** - * Returns and removes all alarms whose timestamp <= the given timestampSec. - * Always updates the registered alarm if return is non-empty. - */ - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> popSoonerThan( - uint32_t timestampSec); - - /** - * Returns the projected alarm timestamp that is registered with - * StatsCompanionService. This may not be equal to the soonest alarm, - * but should be within minDiffToUpdateRegisteredAlarmTimeSec of it. - */ - uint32_t getRegisteredAlarmTimeSec() const { - return mRegisteredAlarmTimeSec; - } - -private: - std::mutex mLock; - - /** - * Timestamp (seconds since epoch) of the alarm registered with - * StatsCompanionService. This, in general, may not be equal to the soonest - * alarm stored in mPq, but should be within minUpdateTimeSec of it. - * A value of 0 indicates that no alarm is currently registered. - */ - uint32_t mRegisteredAlarmTimeSec; - - /** - * Priority queue of alarms, prioritized by soonest alarm.timestampSec. - */ - indexed_priority_queue<InternalAlarm, InternalAlarm::SmallerTimestamp> mPq; - - /** - * Binder interface for communicating with StatsCompanionService. - */ - shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr; - - /** - * Amount by which the soonest projected alarm must differ from - * mRegisteredAlarmTimeSec before updateRegisteredAlarmTime_l is called. - */ - uint32_t mMinUpdateTimeSec; - - /** - * Updates the alarm registered with StatsCompanionService to the given time. - * Also correspondingly updates mRegisteredAlarmTimeSec. - */ - void updateRegisteredAlarmTime_l(uint32_t timestampSec); - - /** - * Cancels the alarm registered with StatsCompanionService. - * Also correspondingly sets mRegisteredAlarmTimeSec to 0. - */ - void cancelRegisteredAlarmTime_l(); - - /** Converts uint32 timestamp in seconds to a Java long in msec. */ - int64_t secToMs(uint32_t timeSec); - - // Callback function to update the alarm via StatsCompanionService. - std::function<void(const shared_ptr<IStatsCompanionService>, int64_t)> mUpdateAlarm; - - // Callback function to cancel the alarm via StatsCompanionService. - std::function<void(const shared_ptr<IStatsCompanionService>)> mCancelAlarm; - -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp deleted file mode 100644 index 6d9beb8f718d..000000000000 --- a/cmds/statsd/src/anomaly/AlarmTracker.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "anomaly/AlarmTracker.h" -#include "anomaly/subscriber_util.h" -#include "HashableDimensionKey.h" -#include "stats_util.h" -#include "storage/StorageManager.h" - -#include <time.h> - -namespace android { -namespace os { -namespace statsd { - -AlarmTracker::AlarmTracker(const int64_t startMillis, - const int64_t currentMillis, - const Alarm& alarm, const ConfigKey& configKey, - const sp<AlarmMonitor>& alarmMonitor) - : mAlarmConfig(alarm), - mConfigKey(configKey), - mAlarmMonitor(alarmMonitor) { - VLOG("AlarmTracker() called"); - mAlarmSec = (startMillis + mAlarmConfig.offset_millis()) / MS_PER_SEC; - // startMillis is the time statsd is created. We need to find the 1st alarm timestamp after - // the config is added to statsd. - mAlarmSec = findNextAlarmSec(currentMillis / MS_PER_SEC); // round up - mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)}; - VLOG("AlarmTracker sets the periodic alarm at: %lld", (long long)mAlarmSec); - if (mAlarmMonitor != nullptr) { - mAlarmMonitor->add(mInternalAlarm); - } -} - -AlarmTracker::~AlarmTracker() { - VLOG("~AlarmTracker() called"); - if (mInternalAlarm != nullptr && mAlarmMonitor != nullptr) { - mAlarmMonitor->remove(mInternalAlarm); - } -} - -void AlarmTracker::addSubscription(const Subscription& subscription) { - mSubscriptions.push_back(subscription); -} - -int64_t AlarmTracker::findNextAlarmSec(int64_t currentTimeSec) { - if (currentTimeSec < mAlarmSec) { - return mAlarmSec; - } - int64_t periodsForward = - ((currentTimeSec - mAlarmSec) * MS_PER_SEC) / mAlarmConfig.period_millis() + 1; - return mAlarmSec + periodsForward * mAlarmConfig.period_millis() / MS_PER_SEC; -} - -void AlarmTracker::informAlarmsFired( - const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) { - if (firedAlarms.empty() || mInternalAlarm == nullptr || - firedAlarms.find(mInternalAlarm) == firedAlarms.end()) { - return; - } - if (!mSubscriptions.empty()) { - VLOG("AlarmTracker triggers the subscribers."); - triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY, - 0 /* metricValue N/A */, mConfigKey, mSubscriptions); - } - firedAlarms.erase(mInternalAlarm); - mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up - mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)}; - VLOG("AlarmTracker sets the periodic alarm at: %lld", (long long)mAlarmSec); - if (mAlarmMonitor != nullptr) { - mAlarmMonitor->add(mInternalAlarm); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h deleted file mode 100644 index 406086da557b..000000000000 --- a/cmds/statsd/src/anomaly/AlarmTracker.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include <gtest/gtest_prod.h> - -#include "AlarmMonitor.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alarm - -#include <stdlib.h> -#include <utils/RefBase.h> - -namespace android { -namespace os { -namespace statsd { - -class AlarmTracker : public virtual RefBase { -public: - AlarmTracker(const int64_t startMillis, - const int64_t currentMillis, - const Alarm& alarm, const ConfigKey& configKey, - const sp<AlarmMonitor>& subscriberAlarmMonitor); - - virtual ~AlarmTracker(); - - void onAlarmFired(); - - void addSubscription(const Subscription& subscription); - - void informAlarmsFired(const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms); - -protected: - // For test only. Returns the alarm timestamp in seconds. Otherwise returns 0. - inline int32_t getAlarmTimestampSec() const { - return mInternalAlarm == nullptr ? 0 : mInternalAlarm->timestampSec; - } - - int64_t findNextAlarmSec(int64_t currentTimeMillis); - - // statsd_config.proto Alarm message that defines this tracker. - const Alarm mAlarmConfig; - - // A reference to the Alarm's config key. - const ConfigKey mConfigKey; - - // The subscriptions that depend on this alarm. - std::vector<Subscription> mSubscriptions; - - // Alarm monitor. - sp<AlarmMonitor> mAlarmMonitor; - - // The current expected alarm time in seconds. - int64_t mAlarmSec; - - // The current alarm. - sp<const InternalAlarm> mInternalAlarm; - - FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp); - FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); - FRIEND_TEST(ConfigUpdateTest, TestUpdateAlarms); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp deleted file mode 100644 index 6aa410b180b8..000000000000 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ /dev/null @@ -1,326 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "AnomalyTracker.h" -#include "external/Perfetto.h" -#include "guardrail/StatsdStats.h" -#include "metadata_util.h" -#include "stats_log_util.h" -#include "subscriber_util.h" -#include "subscriber/IncidentdReporter.h" -#include "subscriber/SubscriberReporter.h" - -#include <inttypes.h> -#include <statslog_statsd.h> -#include <time.h> - -namespace android { -namespace os { -namespace statsd { - -AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey) - : mAlert(alert), mConfigKey(configKey), mNumOfPastBuckets(mAlert.num_buckets() - 1) { - VLOG("AnomalyTracker() called"); - resetStorage(); // initialization -} - -AnomalyTracker::~AnomalyTracker() { - VLOG("~AnomalyTracker() called"); -} - -void AnomalyTracker::onConfigUpdated() { - mSubscriptions.clear(); -} - -void AnomalyTracker::resetStorage() { - VLOG("resetStorage() called."); - mPastBuckets.clear(); - // Excludes the current bucket. - mPastBuckets.resize(mNumOfPastBuckets); - mSumOverPastBuckets.clear(); -} - -size_t AnomalyTracker::index(int64_t bucketNum) const { - if (bucketNum < 0) { - ALOGE("index() was passed a negative bucket number (%lld)!", (long long)bucketNum); - } - return bucketNum % mNumOfPastBuckets; -} - -void AnomalyTracker::advanceMostRecentBucketTo(const int64_t& bucketNum) { - VLOG("advanceMostRecentBucketTo() called."); - if (mNumOfPastBuckets <= 0) { - return; - } - if (bucketNum <= mMostRecentBucketNum) { - ALOGW("Cannot advance buckets backwards (bucketNum=%lld but mMostRecentBucketNum=%lld)", - (long long)bucketNum, (long long)mMostRecentBucketNum); - return; - } - // If in the future (i.e. buckets are ancient), just empty out all past info. - if (bucketNum >= mMostRecentBucketNum + mNumOfPastBuckets) { - resetStorage(); - mMostRecentBucketNum = bucketNum; - return; - } - - // Clear out space by emptying out old mPastBuckets[i] values and update mSumOverPastBuckets. - for (int64_t i = mMostRecentBucketNum + 1; i <= bucketNum; i++) { - const int idx = index(i); - subtractBucketFromSum(mPastBuckets[idx]); - mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket. - } - mMostRecentBucketNum = bucketNum; -} - -void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, - const int64_t& bucketValue, - const int64_t& bucketNum) { - VLOG("addPastBucket(bucketValue) called."); - if (mNumOfPastBuckets == 0 || - bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) { - return; - } - - const int bucketIndex = index(bucketNum); - if (bucketNum <= mMostRecentBucketNum && (mPastBuckets[bucketIndex] != nullptr)) { - // We need to insert into an already existing past bucket. - std::shared_ptr<DimToValMap>& bucket = mPastBuckets[bucketIndex]; - auto itr = bucket->find(key); - if (itr != bucket->end()) { - // Old entry already exists; update it. - subtractValueFromSum(key, itr->second); - itr->second = bucketValue; - } else { - bucket->insert({key, bucketValue}); - } - mSumOverPastBuckets[key] += bucketValue; - } else { - // Bucket does not exist yet (in future or was never made), so we must make it. - std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>(); - bucket->insert({key, bucketValue}); - addPastBucket(bucket, bucketNum); - } -} - -void AnomalyTracker::addPastBucket(std::shared_ptr<DimToValMap> bucket, - const int64_t& bucketNum) { - VLOG("addPastBucket(bucket) called."); - if (mNumOfPastBuckets == 0 || - bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) { - return; - } - - if (bucketNum <= mMostRecentBucketNum) { - // We are updating an old bucket, not adding a new one. - subtractBucketFromSum(mPastBuckets[index(bucketNum)]); - } else { - // Clear space for the new bucket to be at bucketNum. - advanceMostRecentBucketTo(bucketNum); - } - mPastBuckets[index(bucketNum)] = bucket; - addBucketToSum(bucket); -} - -void AnomalyTracker::subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket) { - if (bucket == nullptr) { - return; - } - for (const auto& keyValuePair : *bucket) { - subtractValueFromSum(keyValuePair.first, keyValuePair.second); - } -} - - -void AnomalyTracker::subtractValueFromSum(const MetricDimensionKey& key, - const int64_t& bucketValue) { - auto itr = mSumOverPastBuckets.find(key); - if (itr == mSumOverPastBuckets.end()) { - return; - } - itr->second -= bucketValue; - if (itr->second == 0) { - mSumOverPastBuckets.erase(itr); - } -} - -void AnomalyTracker::addBucketToSum(const shared_ptr<DimToValMap>& bucket) { - if (bucket == nullptr) { - return; - } - // For each dimension present in the bucket, add its value to its corresponding sum. - for (const auto& keyValuePair : *bucket) { - mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second; - } -} - -int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key, - const int64_t& bucketNum) const { - if (bucketNum < 0 || mMostRecentBucketNum < 0 - || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets - || bucketNum > mMostRecentBucketNum) { - return 0; - } - - const auto& bucket = mPastBuckets[index(bucketNum)]; - if (bucket == nullptr) { - return 0; - } - const auto& itr = bucket->find(key); - return itr == bucket->end() ? 0 : itr->second; -} - -int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const { - const auto& itr = mSumOverPastBuckets.find(key); - if (itr != mSumOverPastBuckets.end()) { - return itr->second; - } - return 0; -} - -bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, - const MetricDimensionKey& key, - const int64_t& currentBucketValue) { - - // currentBucketNum should be the next bucket after pastBuckets. If not, advance so that it is. - if (currentBucketNum > mMostRecentBucketNum + 1) { - advanceMostRecentBucketTo(currentBucketNum - 1); - } - return mAlert.has_trigger_if_sum_gt() && - getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); -} - -void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId, - const MetricDimensionKey& key, int64_t metricValue) { - // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on - // real time right now. - if (isInRefractoryPeriod(timestampNs, key)) { - VLOG("Skipping anomaly declaration since within refractory period"); - return; - } - if (mAlert.has_refractory_period_secs()) { - mRefractoryPeriodEndsSec[key] = ((timestampNs + NS_PER_SEC - 1) / NS_PER_SEC) // round up - + mAlert.refractory_period_secs(); - // TODO(b/110563466): If we had access to the bucket_size_millis, consider - // calling resetStorage() - // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) {resetStorage();} - } - - if (!mSubscriptions.empty()) { - ALOGI("An anomaly (%" PRId64 ") %s has occurred! Informing subscribers.", - mAlert.id(), key.toString().c_str()); - informSubscribers(key, metricId, metricValue); - } else { - ALOGI("An anomaly has occurred! (But no subscriber for that alert.)"); - } - - StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id()); - - // TODO(b/110564268): This should also take in the const MetricDimensionKey& key? - util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(), - mConfigKey.GetId(), mAlert.id()); -} - -void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs, - const int64_t& currBucketNum, int64_t metricId, - const MetricDimensionKey& key, - const int64_t& currentBucketValue) { - if (detectAnomaly(currBucketNum, key, currentBucketValue)) { - declareAnomaly(timestampNs, metricId, key, currentBucketValue); - } -} - -bool AnomalyTracker::isInRefractoryPeriod(const int64_t& timestampNs, - const MetricDimensionKey& key) const { - const auto& it = mRefractoryPeriodEndsSec.find(key); - if (it != mRefractoryPeriodEndsSec.end()) { - return timestampNs < (it->second * (int64_t)NS_PER_SEC); - } - return false; -} - -std::pair<bool, uint64_t> AnomalyTracker::getProtoHash() const { - string serializedAlert; - if (!mAlert.SerializeToString(&serializedAlert)) { - ALOGW("Unable to serialize alert %lld", (long long)mAlert.id()); - return {false, 0}; - } - return {true, Hash64(serializedAlert)}; -} - -void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id, - int64_t metricValue) { - triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions); -} - -bool AnomalyTracker::writeAlertMetadataToProto(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::AlertMetadata* alertMetadata) { - bool metadataWritten = false; - - if (mRefractoryPeriodEndsSec.empty()) { - return false; - } - - for (const auto& it: mRefractoryPeriodEndsSec) { - // Do not write the timestamp to disk if it has already expired - if (it.second < systemElapsedTimeNs / NS_PER_SEC) { - continue; - } - - metadataWritten = true; - if (alertMetadata->alert_dim_keyed_data_size() == 0) { - alertMetadata->set_alert_id(mAlert.id()); - } - - metadata::AlertDimensionKeyedData* keyedData = alertMetadata->add_alert_dim_keyed_data(); - // We convert and write the refractory_end_sec to wall clock time because we do not know - // when statsd will start again. - int32_t refractoryEndWallClockSec = (int32_t) ((currentWallClockTimeNs / NS_PER_SEC) + - (it.second - systemElapsedTimeNs / NS_PER_SEC)); - - keyedData->set_last_refractory_ends_sec(refractoryEndWallClockSec); - writeMetricDimensionKeyToMetadataDimensionKey( - it.first, keyedData->mutable_dimension_key()); - } - - return metadataWritten; -} - -void AnomalyTracker::loadAlertMetadata( - const metadata::AlertMetadata& alertMetadata, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - for (const metadata::AlertDimensionKeyedData& keyedData : - alertMetadata.alert_dim_keyed_data()) { - if ((uint64_t) keyedData.last_refractory_ends_sec() < currentWallClockTimeNs / NS_PER_SEC) { - // Do not update the timestamp if it has already expired. - continue; - } - MetricDimensionKey metricKey = loadMetricDimensionKeyFromProto( - keyedData.dimension_key()); - int32_t refractoryPeriodEndsSec = (int32_t) keyedData.last_refractory_ends_sec() - - currentWallClockTimeNs / NS_PER_SEC + systemElapsedTimeNs / NS_PER_SEC; - mRefractoryPeriodEndsSec[metricKey] = refractoryPeriodEndsSec; - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h deleted file mode 100644 index 9a578ee0696d..000000000000 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ /dev/null @@ -1,229 +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. - */ - -#pragma once - -#include <gtest/gtest_prod.h> -#include <stdlib.h> -#include <utils/RefBase.h> - -#include "AlarmMonitor.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert -#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata -#include "hash.h" -#include "stats_util.h" // HashableDimensionKey and DimToValMap - -namespace android { -namespace os { -namespace statsd { - -using std::shared_ptr; -using std::unordered_map; - -// Does NOT allow negative values. -class AnomalyTracker : public virtual RefBase { -public: - AnomalyTracker(const Alert& alert, const ConfigKey& configKey); - - virtual ~AnomalyTracker(); - - // Reset appropriate state on a config update. Clear subscriptions so they can be reset. - void onConfigUpdated(); - - // Add subscriptions that depend on this alert. - void addSubscription(const Subscription& subscription) { - mSubscriptions.push_back(subscription); - } - - // Adds a bucket for the given bucketNum (index starting at 0). - // If a bucket for bucketNum already exists, it will be replaced. - // Also, advances to bucketNum (if not in the past), effectively filling any intervening - // buckets with 0s. - void addPastBucket(std::shared_ptr<DimToValMap> bucket, const int64_t& bucketNum); - - // Inserts (or replaces) the bucket entry for the given bucketNum at the given key to be the - // given bucketValue. If the bucket does not exist, it will be created. - // Also, advances to bucketNum (if not in the past), effectively filling any intervening - // buckets with 0s. - void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue, - const int64_t& bucketNum); - - // Returns true if, based on past buckets plus the new currentBucketValue (which generally - // represents the partially-filled current bucket), an anomaly has happened. - // Also advances to currBucketNum-1. - bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key, - const int64_t& currentBucketValue); - - // Informs incidentd about the detected alert. - void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key, - int64_t metricValue); - - // Detects if, based on past buckets plus the new currentBucketValue (which generally - // represents the partially-filled current bucket), an anomaly has happened, and if so, - // declares an anomaly and informs relevant subscribers. - // Also advances to currBucketNum-1. - void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum, - int64_t metricId, const MetricDimensionKey& key, - const int64_t& currentBucketValue); - - // Init the AlarmMonitor which is shared across anomaly trackers. - virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) { - return; // Base AnomalyTracker class has no need for the AlarmMonitor. - } - - // Returns the sum of all past bucket values for the given dimension key. - int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const; - - // Returns the value for a past bucket, or 0 if that bucket doesn't exist. - int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const; - - // Returns the anomaly threshold set in the configuration. - inline int64_t getAnomalyThreshold() const { - return mAlert.trigger_if_sum_gt(); - } - - // Returns the refractory period ending timestamp (in seconds) for the given key. - // Before this moment, any detected anomaly will be ignored. - // If there is no stored refractory period ending timestamp, returns 0. - uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const { - const auto& it = mRefractoryPeriodEndsSec.find(key); - return it != mRefractoryPeriodEndsSec.end() ? it->second : 0; - } - - // Returns the (constant) number of past buckets this anomaly tracker can store. - inline int getNumOfPastBuckets() const { - return mNumOfPastBuckets; - } - - std::pair<bool, uint64_t> getProtoHash() const; - - // Sets an alarm for the given timestamp. - // Replaces previous alarm if one already exists. - virtual void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) { - return; // The base AnomalyTracker class doesn't have alarms. - } - - // Stops the alarm. - // If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed), - // declare the anomaly now. - virtual void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs) { - return; // The base AnomalyTracker class doesn't have alarms. - } - - // Stop all the alarms owned by this tracker. Does not declare any anomalies. - virtual void cancelAllAlarms() { - return; // The base AnomalyTracker class doesn't have alarms. - } - - // Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker, - // and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor. - virtual void informAlarmsFired(const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) { - return; // The base AnomalyTracker class doesn't have alarms. - } - - // Writes metadata of the alert (refractory_period_end_sec) to AlertMetadata. - // Returns true if at least one element is written to alertMetadata. - bool writeAlertMetadataToProto( - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, metadata::AlertMetadata* alertMetadata); - - void loadAlertMetadata( - const metadata::AlertMetadata& alertMetadata, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs); - -protected: - // For testing only. - // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise - // returns 0. - virtual uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const { - return 0; // The base AnomalyTracker class doesn't have alarms. - } - - // statsd_config.proto Alert message that defines this tracker. - const Alert mAlert; - - // The subscriptions that depend on this alert. - std::vector<Subscription> mSubscriptions; - - // A reference to the Alert's config key. - const ConfigKey mConfigKey; - - // Number of past buckets. One less than the total number of buckets needed - // for the anomaly detection (since the current bucket is not in the past). - const int mNumOfPastBuckets; - - // Values for each of the past mNumOfPastBuckets buckets. Always of size mNumOfPastBuckets. - // mPastBuckets[i] can be null, meaning that no data is present in that bucket. - std::vector<shared_ptr<DimToValMap>> mPastBuckets; - - // Cached sum over all existing buckets in mPastBuckets. - // Its buckets never contain entries of 0. - DimToValMap mSumOverPastBuckets; - - // The bucket number of the last added bucket. - int64_t mMostRecentBucketNum = -1; - - // Map from each dimension to the timestamp that its refractory period (if this anomaly was - // declared for that dimension) ends, in seconds. From this moment and onwards, anomalies - // can be declared again. - // Entries may be, but are not guaranteed to be, removed after the period is finished. - unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec; - - // Advances mMostRecentBucketNum to bucketNum, deleting any data that is now too old. - // Specifically, since it is now too old, removes the data for - // [mMostRecentBucketNum - mNumOfPastBuckets + 1, bucketNum - mNumOfPastBuckets]. - void advanceMostRecentBucketTo(const int64_t& bucketNum); - - // Add the information in the given bucket to mSumOverPastBuckets. - void addBucketToSum(const shared_ptr<DimToValMap>& bucket); - - // Subtract the information in the given bucket from mSumOverPastBuckets - // and remove any items with value 0. - void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket); - - // From mSumOverPastBuckets[key], subtracts bucketValue, removing it if it is now 0. - void subtractValueFromSum(const MetricDimensionKey& key, const int64_t& bucketValue); - - // Returns true if in the refractory period, else false. - bool isInRefractoryPeriod(const int64_t& timestampNs, const MetricDimensionKey& key) const; - - // Calculates the corresponding bucket index within the circular array. - // Requires bucketNum >= 0. - size_t index(int64_t bucketNum) const; - - // Resets all bucket data. For use when all the data gets stale. - virtual void resetStorage(); - - // Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred. - void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue); - - FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets); - FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); - FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection); - FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); - - FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp deleted file mode 100644 index 2b56810170e5..000000000000 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp +++ /dev/null @@ -1,115 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "DurationAnomalyTracker.h" -#include "guardrail/StatsdStats.h" - -namespace android { -namespace os { -namespace statsd { - -DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey, - const sp<AlarmMonitor>& alarmMonitor) - : AnomalyTracker(alert, configKey), mAlarmMonitor(alarmMonitor) { - VLOG("DurationAnomalyTracker() called"); -} - -DurationAnomalyTracker::~DurationAnomalyTracker() { - VLOG("~DurationAnomalyTracker() called"); - cancelAllAlarms(); -} - -void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey, - const int64_t& timestampNs) { - // Alarms are stored in secs. Must round up, since if it fires early, it is ignored completely. - uint32_t timestampSec = static_cast<uint32_t>((timestampNs -1) / NS_PER_SEC) + 1; // round up - if (isInRefractoryPeriod(timestampNs, dimensionKey)) { - VLOG("Not setting anomaly alarm since it would fall in the refractory period."); - return; - } - - auto itr = mAlarms.find(dimensionKey); - if (itr != mAlarms.end() && mAlarmMonitor != nullptr) { - mAlarmMonitor->remove(itr->second); - } - - sp<const InternalAlarm> alarm = new InternalAlarm{timestampSec}; - mAlarms[dimensionKey] = alarm; - if (mAlarmMonitor != nullptr) { - mAlarmMonitor->add(alarm); - } -} - -void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey, - const int64_t& timestampNs) { - const auto itr = mAlarms.find(dimensionKey); - if (itr == mAlarms.end()) { - return; - } - - // If the alarm is set in the past but hasn't fired yet (due to lag), catch it now. - if (itr->second != nullptr && timestampNs >= (int64_t)NS_PER_SEC * itr->second->timestampSec) { - declareAnomaly(timestampNs, mAlert.metric_id(), dimensionKey, - mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - - itr->second->timestampSec); - } - if (mAlarmMonitor != nullptr) { - mAlarmMonitor->remove(itr->second); - } - mAlarms.erase(dimensionKey); -} - -void DurationAnomalyTracker::cancelAllAlarms() { - if (mAlarmMonitor != nullptr) { - for (const auto& itr : mAlarms) { - mAlarmMonitor->remove(itr.second); - } - } - mAlarms.clear(); -} - -void DurationAnomalyTracker::informAlarmsFired(const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) { - - if (firedAlarms.empty() || mAlarms.empty()) return; - // Find the intersection of firedAlarms and mAlarms. - // The for loop is inefficient, since it loops over all keys, but that's okay since it is very - // seldomly called. The alternative would be having InternalAlarms store information about the - // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that - // is rarely ever called. - unordered_map<MetricDimensionKey, sp<const InternalAlarm>> matchedAlarms; - for (const auto& kv : mAlarms) { - if (firedAlarms.count(kv.second) > 0) { - matchedAlarms.insert({kv.first, kv.second}); - } - } - - // Now declare each of these alarms to have fired. - for (const auto& kv : matchedAlarms) { - declareAnomaly( - timestampNs, mAlert.metric_id(), kv.first, - mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - kv.second->timestampSec); - mAlarms.erase(kv.first); - firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it. - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h deleted file mode 100644 index 46419149580b..000000000000 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h +++ /dev/null @@ -1,79 +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. - */ - -#pragma once - -#include "AlarmMonitor.h" -#include "AnomalyTracker.h" - -namespace android { -namespace os { -namespace statsd { - -using std::unordered_map; - -class DurationAnomalyTracker : public virtual AnomalyTracker { -public: - DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey, - const sp<AlarmMonitor>& alarmMonitor); - - virtual ~DurationAnomalyTracker(); - - // Sets an alarm for the given timestamp. - // Replaces previous alarm if one already exists. - void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) override; - - // Stops the alarm. - // If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed), - // declare the anomaly now. - void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs) override; - - // Stop all the alarms owned by this tracker. Does not declare any anomalies. - void cancelAllAlarms() override; - - // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker - // and removes it from firedAlarms. The AlarmMonitor is not informed. - // Note that this will generally be called from a different thread from the other functions; - // the caller is responsible for thread safety. - void informAlarmsFired(const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) override; - -protected: - // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise - // returns 0. - uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const override { - auto it = mAlarms.find(dimensionKey); - return it == mAlarms.end() ? 0 : it->second->timestampSec; - } - - // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they - // are still active. - std::unordered_map<MetricDimensionKey, sp<const InternalAlarm>> mAlarms; - - // Anomaly alarm monitor. - sp<AlarmMonitor> mAlarmMonitor; - - FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/indexed_priority_queue.h b/cmds/statsd/src/anomaly/indexed_priority_queue.h deleted file mode 100644 index 99882d0337b1..000000000000 --- a/cmds/statsd/src/anomaly/indexed_priority_queue.h +++ /dev/null @@ -1,224 +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. - */ - -#pragma once - -#include <utils/RefBase.h> -#include <unordered_map> -#include <vector> - -using namespace android; - -namespace android { -namespace os { -namespace statsd { - -/** Defines a hash function for sp<const AA>, returning the hash of the underlying pointer. */ -template <class AA> -struct SpHash { - size_t operator()(const sp<const AA>& k) const { - return std::hash<const AA*>()(k.get()); - } -}; - -/** - * Min priority queue for generic type AA. - * Unlike a regular priority queue, this class is also capable of removing interior elements. - * @tparam Comparator must implement [bool operator()(sp<const AA> a, sp<const AA> b)], returning - * whether a should be closer to the top of the queue than b. - */ -template <class AA, class Comparator> -class indexed_priority_queue { -public: - indexed_priority_queue(); - /** Adds a into the priority queue. If already present or a==nullptr, does nothing. */ - void push(sp<const AA> a); - /* - * Removes a from the priority queue. If not present or a==nullptr, does nothing. - * Returns true if a had been present (and is now removed), else false. - */ - bool remove(sp<const AA> a); - /** Removes the top element, if there is one. */ - void pop(); - /** Removes all elements. */ - void clear(); - /** Returns whether priority queue contains a (not just a copy of a, but a itself). */ - bool contains(sp<const AA> a) const; - /** Returns min element. Returns nullptr iff empty(). */ - sp<const AA> top() const; - /** Returns number of elements in priority queue. */ - size_t size() const { - return pq.size() - 1; - } // pq is 1-indexed - /** Returns true iff priority queue is empty. */ - bool empty() const { - return size() < 1; - } - -private: - /** Vector representing a min-heap (1-indexed, with nullptr at 0). */ - std::vector<sp<const AA>> pq; - /** Mapping of each element in pq to its index in pq (i.e. the inverse of a=pq[i]). */ - std::unordered_map<sp<const AA>, size_t, SpHash<AA>> indices; - - void init(); - void sift_up(size_t idx); - void sift_down(size_t idx); - /** Returns whether pq[idx1] is considered higher than pq[idx2], according to Comparator. */ - bool higher(size_t idx1, size_t idx2) const; - void swap_indices(size_t i, size_t j); -}; - -// Implementation must be done in this file due to use of template. - -template <class AA, class Comparator> -indexed_priority_queue<AA, Comparator>::indexed_priority_queue() { - init(); -} - -template <class AA, class Comparator> -void indexed_priority_queue<AA, Comparator>::push(sp<const AA> a) { - if (a == nullptr) return; - if (contains(a)) return; - pq.push_back(a); - size_t idx = size(); // index of last element since 1-indexed - indices.insert({a, idx}); - sift_up(idx); // get the pq back in order -} - -template <class AA, class Comparator> -bool indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) { - if (a == nullptr) return false; - if (!contains(a)) return false; - size_t idx = indices[a]; - if (idx >= pq.size()) { - return false; - } - if (idx == size()) { // if a is the last element, i.e. at index idx == size() == (pq.size()-1) - pq.pop_back(); - indices.erase(a); - return true; - } - // move last element (guaranteed not to be at idx) to idx, then delete a - sp<const AA> last_a = pq.back(); - pq[idx] = last_a; - pq.pop_back(); - indices[last_a] = idx; - indices.erase(a); - - // get the heap back in order (since the element at idx is not in order) - sift_up(idx); - sift_down(idx); - - return true; -} - -// The same as, but slightly more efficient than, remove(top()). -template <class AA, class Comparator> -void indexed_priority_queue<AA, Comparator>::pop() { - sp<const AA> a = top(); - if (a == nullptr) return; - const size_t idx = 1; - if (idx == size()) { // if a is the last element - pq.pop_back(); - indices.erase(a); - return; - } - // move last element (guaranteed not to be at idx) to idx, then delete a - sp<const AA> last_a = pq.back(); - pq[idx] = last_a; - pq.pop_back(); - indices[last_a] = idx; - indices.erase(a); - - // get the heap back in order (since the element at idx is not in order) - sift_down(idx); -} - -template <class AA, class Comparator> -void indexed_priority_queue<AA, Comparator>::clear() { - pq.clear(); - indices.clear(); - init(); -} - -template <class AA, class Comparator> -sp<const AA> indexed_priority_queue<AA, Comparator>::top() const { - if (empty()) return nullptr; - return pq[1]; -} - -template <class AA, class Comparator> -void indexed_priority_queue<AA, Comparator>::init() { - pq.push_back(nullptr); // so that pq is 1-indexed. - indices.insert({nullptr, 0}); // just to be consistent with pq. -} - -template <class AA, class Comparator> -void indexed_priority_queue<AA, Comparator>::sift_up(size_t idx) { - while (idx > 1) { - size_t parent = idx / 2; - if (higher(idx, parent)) - swap_indices(idx, parent); - else - break; - idx = parent; - } -} - -template <class AA, class Comparator> -void indexed_priority_queue<AA, Comparator>::sift_down(size_t idx) { - while (2 * idx <= size()) { - size_t child = 2 * idx; - if (child < size() && higher(child + 1, child)) child++; - if (higher(child, idx)) - swap_indices(child, idx); - else - break; - idx = child; - } -} - -template <class AA, class Comparator> -bool indexed_priority_queue<AA, Comparator>::higher(size_t idx1, size_t idx2) const { - if (!(0u < idx1 && idx1 < pq.size() && 0u < idx2 && idx2 < pq.size())) { - return false; // got to do something. - } - return Comparator()(pq[idx1], pq[idx2]); -} - -template <class AA, class Comparator> -bool indexed_priority_queue<AA, Comparator>::contains(sp<const AA> a) const { - if (a == nullptr) return false; // publicly, we pretend that nullptr is not actually in pq. - return indices.count(a) > 0; -} - -template <class AA, class Comparator> -void indexed_priority_queue<AA, Comparator>::swap_indices(size_t i, size_t j) { - if (!(0u < i && i < pq.size() && 0u < j && j < pq.size())) { - return; - } - sp<const AA> val_i = pq[i]; - sp<const AA> val_j = pq[j]; - pq[i] = val_j; - pq[j] = val_i; - indices[val_i] = j; - indices[val_j] = i; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp deleted file mode 100644 index 5a4a41d01de6..000000000000 --- a/cmds/statsd/src/anomaly/subscriber_util.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "external/Perfetto.h" -#include "subscriber/IncidentdReporter.h" -#include "subscriber/SubscriberReporter.h" - -namespace android { -namespace os { -namespace statsd { - -void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey, - int64_t metricValue, const ConfigKey& configKey, - const std::vector<Subscription>& subscriptions) { - VLOG("informSubscribers called."); - if (subscriptions.empty()) { - VLOG("No Subscriptions were associated."); - return; - } - - for (const Subscription& subscription : subscriptions) { - if (subscription.probability_of_informing() < 1 - && ((float)rand() / (float)RAND_MAX) >= subscription.probability_of_informing()) { - // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always. - // The config writer was advised to use -0.1 and 1.1 for never/always. - ALOGI("Fate decided that a subscriber would not be informed."); - continue; - } - switch (subscription.subscriber_information_case()) { - case Subscription::SubscriberInformationCase::kIncidentdDetails: - if (!GenerateIncidentReport(subscription.incidentd_details(), ruleId, metricId, - dimensionKey, metricValue, configKey)) { - ALOGW("Failed to generate incident report."); - } - break; - case Subscription::SubscriberInformationCase::kPerfettoDetails: - if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(), - subscription.id(), ruleId, configKey)) { - ALOGW("Failed to generate perfetto traces."); - } - break; - case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails: - SubscriberReporter::getInstance().alertBroadcastSubscriber(configKey, subscription, - dimensionKey); - break; - default: - break; - } - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h deleted file mode 100644 index 1df3c8991f94..000000000000 --- a/cmds/statsd/src/anomaly/subscriber_util.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include "config/ConfigKey.h" -#include "HashableDimensionKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -namespace android { -namespace os { -namespace statsd { - -void triggerSubscribers(const int64_t ruleId, const int64_t metricId, - const MetricDimensionKey& dimensionKey, int64_t metricValue, - const ConfigKey& configKey, const std::vector<Subscription>& subscriptions); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp deleted file mode 100644 index 4574b2e34547..000000000000 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ /dev/null @@ -1,244 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" -#include "CombinationConditionTracker.h" - -namespace android { -namespace os { -namespace statsd { - -using std::unordered_map; -using std::vector; - -CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index, - const uint64_t protoHash) - : ConditionTracker(id, index, protoHash) { - VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId); -} - -CombinationConditionTracker::~CombinationConditionTracker() { - VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId); -} - -bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionIdIndexMap, - vector<bool>& stack, - vector<ConditionState>& conditionCache) { - VLOG("Combination predicate init() %lld", (long long)mConditionId); - if (mInitialized) { - // All the children are guaranteed to be initialized, but the recursion is needed to - // fill the conditionCache properly, since another combination condition or metric - // might rely on this. The recursion is needed to compute the current condition. - - // Init is called instead of isConditionMet so that the ConditionKey can be filled with the - // default key for sliced conditions, since we do not know all indirect descendants here. - for (const int childIndex : mChildren) { - if (conditionCache[childIndex] == ConditionState::kNotEvaluated) { - allConditionTrackers[childIndex]->init(allConditionConfig, allConditionTrackers, - conditionIdIndexMap, stack, conditionCache); - } - } - conditionCache[mIndex] = - evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); - return true; - } - - // mark this node as visited in the recursion stack. - stack[mIndex] = true; - - Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination(); - - if (!combinationCondition.has_operation()) { - return false; - } - mLogicalOperation = combinationCondition.operation(); - - if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) { - return false; - } - - for (auto child : combinationCondition.predicate()) { - auto it = conditionIdIndexMap.find(child); - - if (it == conditionIdIndexMap.end()) { - ALOGW("Predicate %lld not found in the config", (long long)child); - return false; - } - - int childIndex = it->second; - const auto& childTracker = allConditionTrackers[childIndex]; - // if the child is a visited node in the recursion -> circle detected. - if (stack[childIndex]) { - ALOGW("Circle detected!!!"); - return false; - } - - bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers, - conditionIdIndexMap, stack, conditionCache); - - if (!initChildSucceeded) { - ALOGW("Child initialization failed %lld ", (long long)child); - return false; - } else { - VLOG("Child initialization success %lld ", (long long)child); - } - - if (allConditionTrackers[childIndex]->isSliced()) { - setSliced(true); - mSlicedChildren.push_back(childIndex); - } else { - mUnSlicedChildren.push_back(childIndex); - } - mChildren.push_back(childIndex); - mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(), - childTracker->getAtomMatchingTrackerIndex().end()); - } - - mUnSlicedPartCondition = - evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, conditionCache); - conditionCache[mIndex] = - evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); - - // unmark this node in the recursion stack. - stack[mIndex] = false; - - mInitialized = true; - - return true; -} - -bool CombinationConditionTracker::onConfigUpdated( - const vector<Predicate>& allConditionProtos, const int index, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - const unordered_map<int64_t, int>& conditionTrackerMap) { - ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers, - atomMatchingTrackerMap, conditionTrackerMap); - mTrackerIndex.clear(); - mChildren.clear(); - mUnSlicedChildren.clear(); - mSlicedChildren.clear(); - Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination(); - - for (const int64_t child : combinationCondition.predicate()) { - const auto& it = conditionTrackerMap.find(child); - - if (it == conditionTrackerMap.end()) { - ALOGW("Predicate %lld not found in the config", (long long)child); - return false; - } - - int childIndex = it->second; - const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex]; - - // Ensures that the child's tracker indices are updated. - if (!childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers, - atomMatchingTrackerMap, conditionTrackerMap)) { - ALOGW("Child update failed %lld ", (long long)child); - return false; - } - - if (allConditionTrackers[childIndex]->isSliced()) { - mSlicedChildren.push_back(childIndex); - } else { - mUnSlicedChildren.push_back(childIndex); - } - mChildren.push_back(childIndex); - mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(), - childTracker->getAtomMatchingTrackerIndex().end()); - } - return true; -} - -void CombinationConditionTracker::isConditionMet( - const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, - const bool isPartialLink, - vector<ConditionState>& conditionCache) const { - // So far, this is fine as there is at most one child having sliced output. - for (const int childIndex : mChildren) { - if (conditionCache[childIndex] == ConditionState::kNotEvaluated) { - allConditions[childIndex]->isConditionMet(conditionParameters, allConditions, - isPartialLink, - conditionCache); - } - } - conditionCache[mIndex] = - evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); -} - -void CombinationConditionTracker::evaluateCondition( - const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues, - const std::vector<sp<ConditionTracker>>& mAllConditions, - std::vector<ConditionState>& nonSlicedConditionCache, - std::vector<bool>& conditionChangedCache) { - // value is up to date. - if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) { - return; - } - - for (const int childIndex : mChildren) { - // So far, this is fine as there is at most one child having sliced output. - if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) { - const sp<ConditionTracker>& child = mAllConditions[childIndex]; - child->evaluateCondition(event, eventMatcherValues, mAllConditions, - nonSlicedConditionCache, conditionChangedCache); - } - } - - ConditionState newCondition = - evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache); - if (!mSliced) { - bool nonSlicedChanged = (mUnSlicedPartCondition != newCondition); - mUnSlicedPartCondition = newCondition; - - nonSlicedConditionCache[mIndex] = mUnSlicedPartCondition; - conditionChangedCache[mIndex] = nonSlicedChanged; - } else { - mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, - nonSlicedConditionCache); - - for (const int childIndex : mChildren) { - // If any of the sliced condition in children condition changes, the combination - // condition may be changed too. - if (conditionChangedCache[childIndex]) { - conditionChangedCache[mIndex] = true; - break; - } - } - nonSlicedConditionCache[mIndex] = newCondition; - VLOG("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId, - conditionChangedCache[mIndex] == true); - } -} - -bool CombinationConditionTracker::equalOutputDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - const vector<Matcher>& dimensions) const { - if (mSlicedChildren.size() != 1 || - mSlicedChildren.front() >= (int)allConditions.size() || - mLogicalOperation != LogicalOperation::AND) { - return false; - } - const sp<ConditionTracker>& slicedChild = allConditions.at(mSlicedChildren.front()); - return slicedChild->equalOutputDimensions(allConditions, dimensions); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h deleted file mode 100644 index 672d61c82268..000000000000 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ /dev/null @@ -1,117 +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. - */ - -#ifndef COMBINATION_CONDITION_TRACKER_H -#define COMBINATION_CONDITION_TRACKER_H - -#include "ConditionTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -namespace android { -namespace os { -namespace statsd { - -class CombinationConditionTracker : public ConditionTracker { -public: - CombinationConditionTracker(const int64_t& id, const int index, const uint64_t protoHash); - - ~CombinationConditionTracker(); - - bool init(const std::vector<Predicate>& allConditionConfig, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, - std::vector<ConditionState>& conditionCache) override; - - bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& conditionTrackerMap) override; - - void evaluateCondition(const LogEvent& event, - const std::vector<MatchingState>& eventMatcherValues, - const std::vector<sp<ConditionTracker>>& mAllConditions, - std::vector<ConditionState>& conditionCache, - std::vector<bool>& changedCache) override; - - void isConditionMet(const ConditionKey& conditionParameters, - const std::vector<sp<ConditionTracker>>& allConditions, - const bool isPartialLink, - std::vector<ConditionState>& conditionCache) const override; - - // Only one child predicate can have dimension. - const std::set<HashableDimensionKey>* getChangedToTrueDimensions( - const std::vector<sp<ConditionTracker>>& allConditions) const override { - for (const auto& child : mChildren) { - auto result = allConditions[child]->getChangedToTrueDimensions(allConditions); - if (result != nullptr) { - return result; - } - } - return nullptr; - } - - // Only one child predicate can have dimension. - const std::set<HashableDimensionKey>* getChangedToFalseDimensions( - const std::vector<sp<ConditionTracker>>& allConditions) const override { - for (const auto& child : mChildren) { - auto result = allConditions[child]->getChangedToFalseDimensions(allConditions); - if (result != nullptr) { - return result; - } - } - return nullptr; - } - - bool IsSimpleCondition() const override { return false; } - - bool IsChangedDimensionTrackable() const override { - return mLogicalOperation == LogicalOperation::AND && mSlicedChildren.size() == 1; - } - - bool equalOutputDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - const vector<Matcher>& dimensions) const override; - - void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const override { - if (mSlicedChildren.size() == 1) { - return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions( - allConditions, dimensions); - } - } - - -private: - LogicalOperation mLogicalOperation; - - // Store index of the children Predicates. - // We don't store string name of the Children, because we want to get rid of the hash map to - // map the name to object. We don't want to store smart pointers to children, because it - // increases the risk of circular dependency and memory leak. - std::vector<int> mChildren; - - std::vector<int> mSlicedChildren; - std::vector<int> mUnSlicedChildren; - - FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // COMBINATION_CONDITION_TRACKER_H diff --git a/cmds/statsd/src/condition/ConditionTimer.h b/cmds/statsd/src/condition/ConditionTimer.h deleted file mode 100644 index 1fbe25279736..000000000000 --- a/cmds/statsd/src/condition/ConditionTimer.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include <gtest/gtest_prod.h> -#include <stdint.h> - -namespace android { -namespace os { -namespace statsd { - -/** - * A simple stopwatch to time the duration of condition being true. - * - * The owner of the stopwatch (MetricProducer) is responsible to notify the stopwatch when condition - * changes (start/pause), and when to start a new bucket (a new lap basically). All timestamps - * should be elapsedRealTime in nano seconds. - * - * Keep the timer simple and inline everything. This class is *NOT* thread safe. Caller is - * responsible for thread safety. - */ -class ConditionTimer { -public: - explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) { - if (initCondition) { - mLastConditionChangeTimestampNs = bucketStartNs; - } - }; - - // Tracks how long the condition has been stayed true in the *current* bucket. - // When a new bucket is created, this value will be reset to 0. - int64_t mTimerNs = 0; - - // Last elapsed real timestamp when condition changed. - int64_t mLastConditionChangeTimestampNs = 0; - - bool mCondition = false; - - int64_t newBucketStart(int64_t nextBucketStartNs) { - if (mCondition) { - // Normally, the next bucket happens after the last condition - // change. In this case, add the time between the condition becoming - // true to the next bucket start time. - // Otherwise, the next bucket start time is before the last - // condition change time, this means that the condition was false at - // the bucket boundary before the condition became true, so the - // timer should not get updated and the last condition change time - // remains as is. - if (nextBucketStartNs >= mLastConditionChangeTimestampNs) { - mTimerNs += (nextBucketStartNs - mLastConditionChangeTimestampNs); - mLastConditionChangeTimestampNs = nextBucketStartNs; - } - } else if (mLastConditionChangeTimestampNs > nextBucketStartNs) { - // The next bucket start time is before the last condition change - // time, this means that the condition was true at the bucket - // boundary before the condition became false, so adjust the timer - // to match how long the condition was true to the bucket boundary. - // This means remove the amount the condition stayed true in the - // next bucket from the current bucket. - mTimerNs -= (mLastConditionChangeTimestampNs - nextBucketStartNs); - } - - int64_t temp = mTimerNs; - mTimerNs = 0; - - if (!mCondition && (mLastConditionChangeTimestampNs > nextBucketStartNs)) { - // The next bucket start time is before the last condition change - // time, this means that the condition was true at the bucket - // boundary and remained true in the next bucket up to the condition - // change to false, so adjust the timer to match how long the - // condition stayed true in the next bucket (now the current bucket). - mTimerNs = mLastConditionChangeTimestampNs - nextBucketStartNs; - } - return temp; - } - - void onConditionChanged(bool newCondition, int64_t timestampNs) { - if (newCondition == mCondition) { - return; - } - mCondition = newCondition; - if (newCondition == false) { - mTimerNs += (timestampNs - mLastConditionChangeTimestampNs); - } - mLastConditionChangeTimestampNs = timestampNs; - } - - FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False); - FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_True); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h deleted file mode 100644 index 5a6b8cf334eb..000000000000 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ /dev/null @@ -1,187 +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. - */ - -#pragma once - -#include "condition/condition_util.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/AtomMatchingTracker.h" -#include "matchers/matcher_util.h" - -#include <utils/RefBase.h> - -#include <unordered_map> - -namespace android { -namespace os { -namespace statsd { - -class ConditionTracker : public virtual RefBase { -public: - ConditionTracker(const int64_t& id, const int index, const uint64_t protoHash) - : mConditionId(id), - mIndex(index), - mInitialized(false), - mTrackerIndex(), - mUnSlicedPartCondition(ConditionState::kUnknown), - mSliced(false), - mProtoHash(protoHash){}; - - virtual ~ConditionTracker(){}; - - // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also - // be done in the constructor, but we do it separately because (1) easy to return a bool to - // indicate whether the initialization is successful. (2) makes unit test easier. - // This function can also be called on config updates, in which case it does nothing other than - // fill the condition cache with the current condition. - // allConditionConfig: the list of all Predicate config from statsd_config. - // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also - // need to call init() on child conditions) - // conditionIdIndexMap: the mapping from condition id to its index. - // stack: a bit map to keep track which nodes have been visited on the stack in the recursion. - // conditionCache: tracks initial conditions of all ConditionTrackers. returns the - // current condition if called on a config update. - virtual bool init(const std::vector<Predicate>& allConditionConfig, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionIdIndexMap, - std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) = 0; - - // Update appropriate state on config updates. Primarily, all indices need to be updated. - // This predicate and all of its children are guaranteed to be preserved across the update. - // This function is recursive and will call onConfigUpdated on child conditions. It does not - // manage cycle detection since all preserved conditions should not have any cycles. - // - // allConditionProtos: the new predicates. - // index: the new index of this tracker in allConditionProtos and allConditionTrackers. - // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also - // need to call onConfigUpdated() on child conditions) - // atomMatchingTrackerMap: map of atom matcher id to index after the config update. - // conditionTrackerMap: map of condition tracker id to index after the config update. - // returns whether or not the update is successful. - virtual bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& conditionTrackerMap) { - mIndex = index; - return true; - } - - // evaluate current condition given the new event. - // event: the new log event - // eventMatcherValues: the results of the AtomMatchingTrackers. AtomMatchingTrackers always - // process event before ConditionTrackers, because ConditionTracker depends - // on AtomMatchingTrackers. - // mAllConditions: the list of all ConditionTracker - // conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event. - // conditionChanged: the bit map to record whether the condition has changed. - // If the condition has dimension, then any sub condition changes will report - // conditionChanged. - virtual void evaluateCondition(const LogEvent& event, - const std::vector<MatchingState>& eventMatcherValues, - const std::vector<sp<ConditionTracker>>& mAllConditions, - std::vector<ConditionState>& conditionCache, - std::vector<bool>& conditionChanged) = 0; - - // Query the condition with parameters. - // [conditionParameters]: a map from condition name to the HashableDimensionKey to query the - // condition. - // [allConditions]: all condition trackers. This is needed because the condition evaluation is - // done recursively - // [isPartialLink]: true if the link specified by 'conditionParameters' contains all the fields - // in the condition tracker output dimension. - // [conditionCache]: the cache holding the condition evaluation values. - virtual void isConditionMet( - const ConditionKey& conditionParameters, - const std::vector<sp<ConditionTracker>>& allConditions, - const bool isPartialLink, - std::vector<ConditionState>& conditionCache) const = 0; - - // return the list of AtomMatchingTracker index that this ConditionTracker uses. - virtual const std::set<int>& getAtomMatchingTrackerIndex() const { - return mTrackerIndex; - } - - virtual void setSliced(bool sliced) { - mSliced = mSliced | sliced; - } - - inline bool isSliced() const { - return mSliced; - } - - virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions( - const std::vector<sp<ConditionTracker>>& allConditions) const = 0; - virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions( - const std::vector<sp<ConditionTracker>>& allConditions) const = 0; - - inline int64_t getConditionId() const { - return mConditionId; - } - - inline uint64_t getProtoHash() const { - return mProtoHash; - } - - virtual void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const = 0; - - virtual bool IsChangedDimensionTrackable() const = 0; - - virtual bool IsSimpleCondition() const = 0; - - virtual bool equalOutputDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - const vector<Matcher>& dimensions) const = 0; - - // Return the current condition state of the unsliced part of the condition. - inline ConditionState getUnSlicedPartConditionState() const { - return mUnSlicedPartCondition; - } - -protected: - const int64_t mConditionId; - - // the index of this condition in the manager's condition list. - int mIndex; - - // if it's properly initialized. - bool mInitialized; - - // the list of AtomMatchingTracker index that this ConditionTracker uses. - std::set<int> mTrackerIndex; - - // This variable is only used for CombinationConditionTrackers. - // SimpleConditionTrackers technically don't have an unsliced part because - // they are either sliced or unsliced. - // - // CombinationConditionTrackers have multiple children ConditionTrackers - // that can be a mixture of sliced or unsliced. This tracks the - // condition of the unsliced part of the combination condition. - ConditionState mUnSlicedPartCondition; - - bool mSliced; - - // Hash of the Predicate's proto bytes from StatsdConfig. - // Used to determine if the definition of this condition has changed across a config update. - const uint64_t mProtoHash; - - FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp deleted file mode 100644 index c542032b48ea..000000000000 --- a/cmds/statsd/src/condition/ConditionWizard.cpp +++ /dev/null @@ -1,70 +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. - */ -#include "ConditionWizard.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters, - const bool isPartialLink) { - vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated); - - mAllConditions[index]->isConditionMet( - parameters, mAllConditions, isPartialLink, - cache); - return cache[index]; -} - -const set<HashableDimensionKey>* ConditionWizard::getChangedToTrueDimensions( - const int index) const { - return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions); -} - -const set<HashableDimensionKey>* ConditionWizard::getChangedToFalseDimensions( - const int index) const { - return mAllConditions[index]->getChangedToFalseDimensions(mAllConditions); -} - -bool ConditionWizard::IsChangedDimensionTrackable(const int index) { - if (index >= 0 && index < (int)mAllConditions.size()) { - return mAllConditions[index]->IsChangedDimensionTrackable(); - } else { - return false; - } -} - -bool ConditionWizard::IsSimpleCondition(const int index) { - if (index >= 0 && index < (int)mAllConditions.size()) { - return mAllConditions[index]->IsSimpleCondition(); - } else { - return false; - } -} - -bool ConditionWizard::equalOutputDimensions(const int index, const vector<Matcher>& dimensions) { - if (index >= 0 && index < (int)mAllConditions.size()) { - return mAllConditions[index]->equalOutputDimensions(mAllConditions, dimensions); - } else { - return false; - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h deleted file mode 100644 index 892647910d9f..000000000000 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ /dev/null @@ -1,68 +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. - */ - -#ifndef CONDITION_WIZARD_H -#define CONDITION_WIZARD_H - -#include "ConditionTracker.h" -#include "condition_util.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -// Held by MetricProducer, to query a condition state with input defined in MetricConditionLink. -class ConditionWizard : public virtual android::RefBase { -public: - ConditionWizard(){}; // for testing - explicit ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers) - : mAllConditions(conditionTrackers){}; - - virtual ~ConditionWizard(){}; - - // Query condition state, for a ConditionTracker at [conditionIndex], with [conditionParameters] - // [conditionParameters] mapping from condition name to the HashableDimensionKey to query the - // condition. - // The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case, - // the conditionParameters contains the parameters for it's children SimpleConditionTrackers. - virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters, - const bool isPartialLink); - - virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(const int index) const; - virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions( - const int index) const; - bool equalOutputDimensions(const int index, const vector<Matcher>& dimensions); - - bool IsChangedDimensionTrackable(const int index); - bool IsSimpleCondition(const int index); - - ConditionState getUnSlicedPartConditionState(const int index) { - return mAllConditions[index]->getUnSlicedPartConditionState(); - } - void getTrueSlicedDimensions(const int index, - std::set<HashableDimensionKey>* trueDimensions) const { - return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions); - } - -private: - std::vector<sp<ConditionTracker>> mAllConditions; -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // CONDITION_WIZARD_H diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp deleted file mode 100644 index 1dcc8f96131a..000000000000 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ /dev/null @@ -1,409 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "SimpleConditionTracker.h" -#include "guardrail/StatsdStats.h" - -namespace android { -namespace os { -namespace statsd { - -using std::unordered_map; - -SimpleConditionTracker::SimpleConditionTracker( - const ConfigKey& key, const int64_t& id, const uint64_t protoHash, const int index, - const SimplePredicate& simplePredicate, - const unordered_map<int64_t, int>& atomMatchingTrackerMap) - : ConditionTracker(id, index, protoHash), - mConfigKey(key), - mContainANYPositionInInternalDimensions(false) { - VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId); - mCountNesting = simplePredicate.count_nesting(); - - setMatcherIndices(simplePredicate, atomMatchingTrackerMap); - - if (simplePredicate.has_dimensions()) { - translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions); - if (mOutputDimensions.size() > 0) { - mSliced = true; - } - mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions()); - } - - if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) { - mInitialValue = ConditionState::kFalse; - } else { - mInitialValue = ConditionState::kUnknown; - } - - mInitialized = true; -} - -SimpleConditionTracker::~SimpleConditionTracker() { - VLOG("~SimpleConditionTracker()"); -} - -bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionIdIndexMap, - vector<bool>& stack, vector<ConditionState>& conditionCache) { - // SimpleConditionTracker does not have dependency on other conditions, thus we just return - // if the initialization was successful. - ConditionKey conditionKey; - if (mSliced) { - conditionKey[mConditionId] = DEFAULT_DIMENSION_KEY; - } - isConditionMet(conditionKey, allConditionTrackers, mSliced, conditionCache); - return mInitialized; -} - -bool SimpleConditionTracker::onConfigUpdated( - const vector<Predicate>& allConditionProtos, const int index, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - const unordered_map<int64_t, int>& conditionTrackerMap) { - ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers, - atomMatchingTrackerMap, conditionTrackerMap); - setMatcherIndices(allConditionProtos[index].simple_predicate(), atomMatchingTrackerMap); - return true; -} - -void SimpleConditionTracker::setMatcherIndices( - const SimplePredicate& simplePredicate, - const unordered_map<int64_t, int>& atomMatchingTrackerMap) { - mTrackerIndex.clear(); - if (simplePredicate.has_start()) { - auto pair = atomMatchingTrackerMap.find(simplePredicate.start()); - if (pair == atomMatchingTrackerMap.end()) { - ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start()); - return; - } - mStartLogMatcherIndex = pair->second; - mTrackerIndex.insert(mStartLogMatcherIndex); - } else { - mStartLogMatcherIndex = -1; - } - - if (simplePredicate.has_stop()) { - auto pair = atomMatchingTrackerMap.find(simplePredicate.stop()); - if (pair == atomMatchingTrackerMap.end()) { - ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop()); - return; - } - mStopLogMatcherIndex = pair->second; - mTrackerIndex.insert(mStopLogMatcherIndex); - } else { - mStopLogMatcherIndex = -1; - } - - if (simplePredicate.has_stop_all()) { - auto pair = atomMatchingTrackerMap.find(simplePredicate.stop_all()); - if (pair == atomMatchingTrackerMap.end()) { - ALOGW("Stop all matcher %lld found in the config", - (long long)simplePredicate.stop_all()); - return; - } - mStopAllLogMatcherIndex = pair->second; - mTrackerIndex.insert(mStopAllLogMatcherIndex); - } else { - mStopAllLogMatcherIndex = -1; - } -} - -void SimpleConditionTracker::dumpState() { - VLOG("%lld DUMP:", (long long)mConditionId); - for (const auto& pair : mSlicedConditionState) { - VLOG("\t%s : %d", pair.first.toString().c_str(), pair.second); - } - - VLOG("Changed to true keys: \n"); - for (const auto& key : mLastChangedToTrueDimensions) { - VLOG("%s", key.toString().c_str()); - } - VLOG("Changed to false keys: \n"); - for (const auto& key : mLastChangedToFalseDimensions) { - VLOG("%s", key.toString().c_str()); - } -} - -void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache, - std::vector<bool>& conditionChangedCache) { - // Unless the default condition is false, and there was nothing started, otherwise we have - // triggered a condition change. - conditionChangedCache[mIndex] = - (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false - : true; - - for (const auto& cond : mSlicedConditionState) { - if (cond.second > 0) { - mLastChangedToFalseDimensions.insert(cond.first); - } - } - - // After StopAll, we know everything has stopped. From now on, default condition is false. - mInitialValue = ConditionState::kFalse; - mSlicedConditionState.clear(); - conditionCache[mIndex] = ConditionState::kFalse; -} - -bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) { - if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) { - // if the condition is not sliced or the key is not new, we are good! - return false; - } - // 1. Report the tuple count if the tuple count > soft limit - if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mSlicedConditionState.size() + 1; - StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("Predicate %lld dropping data for dimension key %s", - (long long)mConditionId, newKey.toString().c_str()); - return true; - } - } - return false; -} - -void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey, - bool matchStart, ConditionState* conditionCache, - bool* conditionChangedCache) { - bool changed = false; - auto outputIt = mSlicedConditionState.find(outputKey); - ConditionState newCondition; - if (hitGuardRail(outputKey)) { - (*conditionChangedCache) = false; - // Tells the caller it's evaluated. - (*conditionCache) = ConditionState::kUnknown; - return; - } - if (outputIt == mSlicedConditionState.end()) { - // We get a new output key. - newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse; - if (matchStart && mInitialValue != ConditionState::kTrue) { - mSlicedConditionState[outputKey] = 1; - changed = true; - mLastChangedToTrueDimensions.insert(outputKey); - } else if (mInitialValue != ConditionState::kFalse) { - // it's a stop and we don't have history about it. - // If the default condition is not false, it means this stop is valuable to us. - mSlicedConditionState[outputKey] = 0; - mLastChangedToFalseDimensions.insert(outputKey); - changed = true; - } - } else { - // we have history about this output key. - auto& startedCount = outputIt->second; - // assign the old value first. - newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse; - if (matchStart) { - if (startedCount == 0) { - mLastChangedToTrueDimensions.insert(outputKey); - // This condition for this output key will change from false -> true - changed = true; - } - - // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated - // as 1 if not counting nesting. - startedCount++; - newCondition = ConditionState::kTrue; - } else { - // This is a stop event. - if (startedCount > 0) { - if (mCountNesting) { - startedCount--; - if (startedCount == 0) { - newCondition = ConditionState::kFalse; - } - } else { - // not counting nesting, so ignore the number of starts, stop now. - startedCount = 0; - newCondition = ConditionState::kFalse; - } - // if everything has stopped for this output key, condition true -> false; - if (startedCount == 0) { - mLastChangedToFalseDimensions.insert(outputKey); - changed = true; - } - } - - // if default condition is false, it means we don't need to keep the false values. - if (mInitialValue == ConditionState::kFalse && startedCount == 0) { - mSlicedConditionState.erase(outputIt); - VLOG("erase key %s", outputKey.toString().c_str()); - } - } - } - - // dump all dimensions for debugging - if (DEBUG) { - dumpState(); - } - - (*conditionChangedCache) = changed; - (*conditionCache) = newCondition; - - VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId, - conditionChangedCache[mIndex] == true); -} - -void SimpleConditionTracker::evaluateCondition( - const LogEvent& event, - const vector<MatchingState>& eventMatcherValues, - const vector<sp<ConditionTracker>>& mAllConditions, - vector<ConditionState>& conditionCache, - vector<bool>& conditionChangedCache) { - if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { - // it has been evaluated. - VLOG("Yes, already evaluated, %lld %d", - (long long)mConditionId, conditionCache[mIndex]); - return; - } - mLastChangedToTrueDimensions.clear(); - mLastChangedToFalseDimensions.clear(); - - if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) && - eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) { - handleStopAll(conditionCache, conditionChangedCache); - return; - } - - int matchedState = -1; - // Note: The order to evaluate the following start, stop, stop_all matters. - // The priority of overwrite is stop_all > stop > start. - if (mStartLogMatcherIndex >= 0 && - eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) { - matchedState = 1; - } - - if (mStopLogMatcherIndex >= 0 && - eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) { - matchedState = 0; - } - - if (matchedState < 0) { - // The event doesn't match this condition. So we just report existing condition values. - conditionChangedCache[mIndex] = false; - if (mSliced) { - // if the condition result is sliced. The overall condition is true if any of the sliced - // condition is true - conditionCache[mIndex] = mInitialValue; - for (const auto& slicedCondition : mSlicedConditionState) { - if (slicedCondition.second > 0) { - conditionCache[mIndex] = ConditionState::kTrue; - break; - } - } - } else { - const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY); - if (itr == mSlicedConditionState.end()) { - // condition not sliced, but we haven't seen the matched start or stop yet. so - // return initial value. - conditionCache[mIndex] = mInitialValue; - } else { - // return the cached condition. - conditionCache[mIndex] = - itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - } - } - return; - } - - ConditionState overallState = mInitialValue; - bool overallChanged = false; - - if (mOutputDimensions.size() == 0) { - handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState, - &overallChanged); - } else if (!mContainANYPositionInInternalDimensions) { - HashableDimensionKey outputValue; - filterValues(mOutputDimensions, event.getValues(), &outputValue); - - // If this event has multiple nodes in the attribution chain, this log event probably will - // generate multiple dimensions. If so, we will find if the condition changes for any - // dimension and ask the corresponding metric producer to verify whether the actual sliced - // condition has changed or not. - // A high level assumption is that a predicate is either sliced or unsliced. We will never - // have both sliced and unsliced version of a predicate. - handleConditionEvent(outputValue, matchedState == 1, &overallState, &overallChanged); - } else { - ALOGE("The condition tracker should not be sliced by ANY position matcher."); - } - conditionCache[mIndex] = overallState; - conditionChangedCache[mIndex] = overallChanged; -} - -void SimpleConditionTracker::isConditionMet( - const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, - const bool isPartialLink, - vector<ConditionState>& conditionCache) const { - - if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { - // it has been evaluated. - VLOG("Yes, already evaluated, %lld %d", - (long long)mConditionId, conditionCache[mIndex]); - return; - } - const auto pair = conditionParameters.find(mConditionId); - - if (pair == conditionParameters.end()) { - ConditionState conditionState = ConditionState::kNotEvaluated; - conditionState = conditionState | mInitialValue; - if (!mSliced) { - const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY); - if (itr != mSlicedConditionState.end()) { - ConditionState sliceState = - itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - conditionState = conditionState | sliceState; - } - } - conditionCache[mIndex] = conditionState; - return; - } - - ConditionState conditionState = ConditionState::kNotEvaluated; - const HashableDimensionKey& key = pair->second; - if (isPartialLink) { - // For unseen key, check whether the require dimensions are subset of sliced condition - // output. - conditionState = conditionState | mInitialValue; - for (const auto& slice : mSlicedConditionState) { - ConditionState sliceState = - slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - if (slice.first.contains(key)) { - conditionState = conditionState | sliceState; - } - } - } else { - auto startedCountIt = mSlicedConditionState.find(key); - conditionState = conditionState | mInitialValue; - if (startedCountIt != mSlicedConditionState.end()) { - ConditionState sliceState = - startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - conditionState = conditionState | sliceState; - } - - } - conditionCache[mIndex] = conditionState; - VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h deleted file mode 100644 index 7a8b40108448..000000000000 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ /dev/null @@ -1,145 +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. - */ - -#ifndef SIMPLE_CONDITION_TRACKER_H -#define SIMPLE_CONDITION_TRACKER_H - -#include <gtest/gtest_prod.h> -#include "ConditionTracker.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -class SimpleConditionTracker : public ConditionTracker { -public: - SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const uint64_t protoHash, - const int index, const SimplePredicate& simplePredicate, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap); - - ~SimpleConditionTracker(); - - bool init(const std::vector<Predicate>& allConditionConfig, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, - std::vector<ConditionState>& conditionCache) override; - - bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& conditionTrackerMap) override; - - void evaluateCondition(const LogEvent& event, - const std::vector<MatchingState>& eventMatcherValues, - const std::vector<sp<ConditionTracker>>& mAllConditions, - std::vector<ConditionState>& conditionCache, - std::vector<bool>& changedCache) override; - - void isConditionMet(const ConditionKey& conditionParameters, - const std::vector<sp<ConditionTracker>>& allConditions, - const bool isPartialLink, - std::vector<ConditionState>& conditionCache) const override; - - virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions( - const std::vector<sp<ConditionTracker>>& allConditions) const { - if (mSliced) { - return &mLastChangedToTrueDimensions; - } else { - return nullptr; - } - } - - virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions( - const std::vector<sp<ConditionTracker>>& allConditions) const { - if (mSliced) { - return &mLastChangedToFalseDimensions; - } else { - return nullptr; - } - } - - void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const override { - for (const auto& itr : mSlicedConditionState) { - if (itr.second > 0) { - dimensions->insert(itr.first); - } - } - } - - bool IsChangedDimensionTrackable() const override { return true; } - - bool IsSimpleCondition() const override { return true; } - - bool equalOutputDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - const vector<Matcher>& dimensions) const override { - return equalDimensions(mOutputDimensions, dimensions); - } - -private: - const ConfigKey mConfigKey; - // The index of the LogEventMatcher which defines the start. - int mStartLogMatcherIndex; - - // The index of the LogEventMatcher which defines the end. - int mStopLogMatcherIndex; - - // if the start end needs to be nested. - bool mCountNesting; - - // The index of the LogEventMatcher which defines the stop all. - int mStopAllLogMatcherIndex; - - ConditionState mInitialValue; - - std::vector<Matcher> mOutputDimensions; - - bool mContainANYPositionInInternalDimensions; - - std::set<HashableDimensionKey> mLastChangedToTrueDimensions; - std::set<HashableDimensionKey> mLastChangedToFalseDimensions; - - std::map<HashableDimensionKey, int> mSlicedConditionState; - - void setMatcherIndices(const SimplePredicate& predicate, - const std::unordered_map<int64_t, int>& logTrackerMap); - - void handleStopAll(std::vector<ConditionState>& conditionCache, - std::vector<bool>& changedCache); - - void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart, - ConditionState* conditionCache, bool* changedCache); - - bool hitGuardRail(const HashableDimensionKey& newKey); - - void dumpState(); - - FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition); - FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim); - FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll); - FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // SIMPLE_CONDITION_TRACKER_H diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp deleted file mode 100644 index 60b8c53e91e1..000000000000 --- a/cmds/statsd/src/condition/condition_util.cpp +++ /dev/null @@ -1,93 +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. - */ - -#include "Log.h" - -#include "condition_util.h" - -#include "../matchers/matcher_util.h" -#include "ConditionTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - - -ConditionState evaluateCombinationCondition(const std::vector<int>& children, - const LogicalOperation& operation, - const std::vector<ConditionState>& conditionCache) { - ConditionState newCondition; - - bool hasUnknown = false; - bool hasFalse = false; - bool hasTrue = false; - - for (auto childIndex : children) { - ConditionState childState = conditionCache[childIndex]; - if (childState == ConditionState::kUnknown) { - hasUnknown = true; - break; - } - if (childState == ConditionState::kFalse) { - hasFalse = true; - } - if (childState == ConditionState::kTrue) { - hasTrue = true; - } - } - - // If any child condition is in unknown state, the condition is unknown too. - if (hasUnknown) { - return ConditionState::kUnknown; - } - - switch (operation) { - case LogicalOperation::AND: { - newCondition = hasFalse ? ConditionState::kFalse : ConditionState::kTrue; - break; - } - case LogicalOperation::OR: { - newCondition = hasTrue ? ConditionState::kTrue : ConditionState::kFalse; - break; - } - case LogicalOperation::NOT: - newCondition = children.empty() ? ConditionState::kUnknown : - ((conditionCache[children[0]] == ConditionState::kFalse) ? - ConditionState::kTrue : ConditionState::kFalse); - break; - case LogicalOperation::NAND: - newCondition = hasFalse ? ConditionState::kTrue : ConditionState::kFalse; - break; - case LogicalOperation::NOR: - newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue; - break; - case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED: - newCondition = ConditionState::kFalse; - break; - } - return newCondition; -} - -ConditionState operator|(ConditionState l, ConditionState r) { - return l >= r ? l : r; -} -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h deleted file mode 100644 index fed90ec3da37..000000000000 --- a/cmds/statsd/src/condition/condition_util.h +++ /dev/null @@ -1,43 +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. - */ - -#ifndef CONDITION_UTIL_H -#define CONDITION_UTIL_H - -#include <vector> -#include "../matchers/matcher_util.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -namespace android { -namespace os { -namespace statsd { - -enum ConditionState { - kNotEvaluated = -2, - kUnknown = -1, - kFalse = 0, - kTrue = 1, -}; - -ConditionState operator|(ConditionState l, ConditionState r); - -ConditionState evaluateCombinationCondition(const std::vector<int>& children, - const LogicalOperation& operation, - const std::vector<ConditionState>& conditionCache); -} // namespace statsd -} // namespace os -} // namespace android -#endif // CONDITION_UTIL_H diff --git a/cmds/statsd/src/config/ConfigKey.cpp b/cmds/statsd/src/config/ConfigKey.cpp deleted file mode 100644 index 4a2bd2799df8..000000000000 --- a/cmds/statsd/src/config/ConfigKey.cpp +++ /dev/null @@ -1,54 +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. - */ - -#include "config/ConfigKey.h" - -namespace android { -namespace os { -namespace statsd { - -ConfigKey::ConfigKey() { -} - -ConfigKey::ConfigKey(const ConfigKey& that) : mId(that.mId), mUid(that.mUid) { -} - -ConfigKey::ConfigKey(int uid, const int64_t& id) : mId(id), mUid(uid) { -} - -ConfigKey::~ConfigKey() { -} - -string ConfigKey::ToString() const { - string s; - s += "(" + std::to_string(mUid) + " " + std::to_string(mId) + ")"; - return s; -} - - -int64_t StrToInt64(const string& str) { - char* endp; - int64_t value; - value = strtoll(str.c_str(), &endp, 0); - if (endp == str.c_str() || *endp != '\0') { - value = 0; - } - return value; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h deleted file mode 100644 index 4cc9393fbd02..000000000000 --- a/cmds/statsd/src/config/ConfigKey.h +++ /dev/null @@ -1,89 +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. - */ - -#pragma once - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -#include <string> - -namespace android { -namespace os { -namespace statsd { - -using std::hash; -using std::string; - -/** - * Uniquely identifies a configuration. - */ -class ConfigKey { -public: - ConfigKey(); - ConfigKey(const ConfigKey& that); - ConfigKey(int uid, const int64_t& id); - ~ConfigKey(); - - inline int GetUid() const { - return mUid; - } - inline const int64_t& GetId() const { - return mId; - } - - inline bool operator<(const ConfigKey& that) const { - if (mUid < that.mUid) { - return true; - } - if (mUid > that.mUid) { - return false; - } - return mId < that.mId; - }; - - inline bool operator==(const ConfigKey& that) const { - return mUid == that.mUid && mId == that.mId; - }; - - string ToString() const; - -private: - int64_t mId; - int mUid; -}; - -int64_t StrToInt64(const string& str); - -} // namespace statsd -} // namespace os -} // namespace android - -/** - * A hash function for ConfigKey so it can be used for unordered_map/set. - * Unfortunately this has to go in std namespace because C++ is fun! - */ -namespace std { - -using android::os::statsd::ConfigKey; - -template <> -struct hash<ConfigKey> { - std::size_t operator()(const ConfigKey& key) const { - return (7 * key.GetUid()) ^ ((hash<long long>()(key.GetId()))); - } -}; - -} // namespace std diff --git a/cmds/statsd/src/config/ConfigListener.cpp b/cmds/statsd/src/config/ConfigListener.cpp deleted file mode 100644 index 21a3f1673fd7..000000000000 --- a/cmds/statsd/src/config/ConfigListener.cpp +++ /dev/null @@ -1,31 +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. - */ - -#include "config/ConfigListener.h" - -namespace android { -namespace os { -namespace statsd { - -ConfigListener::ConfigListener() { -} - -ConfigListener::~ConfigListener() { -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/config/ConfigListener.h b/cmds/statsd/src/config/ConfigListener.h deleted file mode 100644 index 3d301379f359..000000000000 --- a/cmds/statsd/src/config/ConfigListener.h +++ /dev/null @@ -1,52 +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. - */ - -#pragma once - -#include "config/ConfigKey.h" - -#include <utils/RefBase.h> - -namespace android { -namespace os { -namespace statsd { - -using android::RefBase; - -/** - * Callback for different subsystems inside statsd to implement to find out - * when a configuration has been added, updated or removed. - */ -class ConfigListener : public virtual RefBase { -public: - ConfigListener(); - virtual ~ConfigListener(); - - /** - * A configuration was added or updated. - */ - virtual void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config, bool modularUpdate = false) = 0; - - /** - * A configuration was removed. - */ - virtual void OnConfigRemoved(const ConfigKey& key) = 0; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp deleted file mode 100644 index 13020e06dc5d..000000000000 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ /dev/null @@ -1,375 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "config/ConfigManager.h" -#include "storage/StorageManager.h" - -#include "guardrail/StatsdStats.h" -#include "stats_log_util.h" -#include "stats_util.h" -#include "stats_log_util.h" - -#include <stdio.h> -#include <vector> -#include "android-base/stringprintf.h" - -namespace android { -namespace os { -namespace statsd { - -using std::pair; -using std::string; -using std::vector; - -#define STATS_SERVICE_DIR "/data/misc/stats-service" - -using android::base::StringPrintf; -using std::unique_ptr; - -struct ConfigReceiverDeathCookie { - ConfigReceiverDeathCookie(const wp<ConfigManager>& configManager, const ConfigKey& configKey, - const shared_ptr<IPendingIntentRef>& pir) : - mConfigManager(configManager), mConfigKey(configKey), mPir(pir) { - } - - wp<ConfigManager> mConfigManager; - ConfigKey mConfigKey; - shared_ptr<IPendingIntentRef> mPir; -}; - -void ConfigManager::configReceiverDied(void* cookie) { - auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie); - sp<ConfigManager> thiz = cookie_->mConfigManager.promote(); - if (!thiz) { - return; - } - - 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. - delete cookie_; -} - -struct ActiveConfigChangedReceiverDeathCookie { - ActiveConfigChangedReceiverDeathCookie(const wp<ConfigManager>& configManager, const int uid, - const shared_ptr<IPendingIntentRef>& pir) : - mConfigManager(configManager), mUid(uid), mPir(pir) { - } - - wp<ConfigManager> mConfigManager; - int mUid; - shared_ptr<IPendingIntentRef> mPir; -}; - -void ConfigManager::activeConfigChangedReceiverDied(void* cookie) { - auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie); - sp<ConfigManager> thiz = cookie_->mConfigManager.promote(); - if (!thiz) { - return; - } - - int uid = cookie_->mUid; - 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() : - mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)), - mActiveConfigChangedReceiverDeathRecipient( - AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) { -} - -ConfigManager::~ConfigManager() { -} - -void ConfigManager::Startup() { - map<ConfigKey, StatsdConfig> configsFromDisk; - StorageManager::readConfigFromDisk(configsFromDisk); - for (const auto& pair : configsFromDisk) { - UpdateConfig(pair.first, pair.second); - } -} - -void ConfigManager::StartupForTest() { - // No-op function to avoid reading configs from disks for tests. -} - -void ConfigManager::AddListener(const sp<ConfigListener>& listener) { - lock_guard<mutex> lock(mMutex); - mListeners.push_back(listener); -} - -void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& config) { - vector<sp<ConfigListener>> broadcastList; - { - lock_guard <mutex> lock(mMutex); - - const int numBytes = config.ByteSize(); - vector<uint8_t> buffer(numBytes); - config.SerializeToArray(&buffer[0], numBytes); - - auto uidIt = mConfigs.find(key.GetUid()); - // GuardRail: Limit the number of configs per uid. - if (uidIt != mConfigs.end()) { - auto it = uidIt->second.find(key); - if (it == uidIt->second.end() && - uidIt->second.size() >= StatsdStats::kMaxConfigCountPerUid) { - ALOGE("ConfigManager: uid %d has exceeded the config count limit", key.GetUid()); - return; - } - } - - // Check if it's a duplicate config. - if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end() && - StorageManager::hasIdenticalConfig(key, buffer)) { - // This is a duplicate config. - ALOGI("ConfigManager This is a duplicate config %s", key.ToString().c_str()); - // Update saved file on disk. We still update timestamp of file when - // there exists a duplicate configuration to avoid garbage collection. - update_saved_configs_locked(key, buffer, numBytes); - return; - } - - // Update saved file on disk. - update_saved_configs_locked(key, buffer, numBytes); - - // Add to set. - mConfigs[key.GetUid()].insert(key); - - for (const sp<ConfigListener>& listener : mListeners) { - broadcastList.push_back(listener); - } - } - - const int64_t timestampNs = getElapsedRealtimeNs(); - // Tell everyone - for (const sp<ConfigListener>& listener : broadcastList) { - listener->OnConfigUpdated(timestampNs, key, config); - } -} - -void ConfigManager::SetConfigReceiver(const ConfigKey& key, - const shared_ptr<IPendingIntentRef>& pir) { - lock_guard<mutex> lock(mMutex); - mConfigReceivers[key] = pir; - AIBinder_linkToDeath(pir->asBinder().get(), mConfigReceiverDeathRecipient.get(), - new ConfigReceiverDeathCookie(this, key, pir)); -} - -void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) { - lock_guard<mutex> lock(mMutex); - mConfigReceivers.erase(key); -} - -void ConfigManager::SetActiveConfigsChangedReceiver(const int uid, - const shared_ptr<IPendingIntentRef>& pir) { - { - lock_guard<mutex> lock(mMutex); - mActiveConfigsChangedReceivers[uid] = pir; - } - AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(), - new ActiveConfigChangedReceiverDeathCookie(this, uid, pir)); -} - -void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) { - lock_guard<mutex> lock(mMutex); - mActiveConfigsChangedReceivers.erase(uid); -} - -void ConfigManager::RemoveConfig(const ConfigKey& key) { - vector<sp<ConfigListener>> broadcastList; - { - lock_guard <mutex> lock(mMutex); - - auto uid = key.GetUid(); - auto uidIt = mConfigs.find(uid); - if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) { - // Remove from map - uidIt->second.erase(key); - - for (const sp<ConfigListener>& listener : mListeners) { - broadcastList.push_back(listener); - } - } - - // Remove from disk. There can still be a lingering file on disk so we check - // whether or not the config was on memory. - remove_saved_configs(key); - } - - for (const sp<ConfigListener>& listener:broadcastList) { - listener->OnConfigRemoved(key); - } -} - -void ConfigManager::remove_saved_configs(const ConfigKey& key) { - string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()); - StorageManager::deleteSuffixedFiles(STATS_SERVICE_DIR, suffix.c_str()); -} - -void ConfigManager::RemoveConfigs(int uid) { - vector<ConfigKey> removed; - vector<sp<ConfigListener>> broadcastList; - { - lock_guard <mutex> lock(mMutex); - - auto uidIt = mConfigs.find(uid); - if (uidIt == mConfigs.end()) { - return; - } - - for (auto it = uidIt->second.begin(); it != uidIt->second.end(); ++it) { - // Remove from map - remove_saved_configs(*it); - removed.push_back(*it); - } - - mConfigs.erase(uidIt); - - for (const sp<ConfigListener>& listener : mListeners) { - broadcastList.push_back(listener); - } - } - - // Remove separately so if they do anything in the callback they can't mess up our iteration. - for (auto& key : removed) { - // Tell everyone - for (const sp<ConfigListener>& listener:broadcastList) { - listener->OnConfigRemoved(key); - } - } -} - -void ConfigManager::RemoveAllConfigs() { - vector<ConfigKey> removed; - vector<sp<ConfigListener>> broadcastList; - { - lock_guard <mutex> lock(mMutex); - - for (auto uidIt = mConfigs.begin(); uidIt != mConfigs.end();) { - for (auto it = uidIt->second.begin(); it != uidIt->second.end();) { - // Remove from map - removed.push_back(*it); - it = uidIt->second.erase(it); - } - uidIt = mConfigs.erase(uidIt); - } - - for (const sp<ConfigListener>& listener : mListeners) { - broadcastList.push_back(listener); - } - } - - // Remove separately so if they do anything in the callback they can't mess up our iteration. - for (auto& key : removed) { - // Tell everyone - for (const sp<ConfigListener>& listener:broadcastList) { - listener->OnConfigRemoved(key); - } - } -} - -vector<ConfigKey> ConfigManager::GetAllConfigKeys() const { - lock_guard<mutex> lock(mMutex); - - vector<ConfigKey> ret; - for (auto uidIt = mConfigs.cbegin(); uidIt != mConfigs.cend(); ++uidIt) { - for (auto it = uidIt->second.cbegin(); it != uidIt->second.cend(); ++it) { - ret.push_back(*it); - } - } - return ret; -} - -const shared_ptr<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const { - lock_guard<mutex> lock(mMutex); - - auto it = mConfigReceivers.find(key); - if (it == mConfigReceivers.end()) { - return nullptr; - } else { - return it->second; - } -} - -const shared_ptr<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) - const { - lock_guard<mutex> lock(mMutex); - - auto it = mActiveConfigsChangedReceivers.find(uid); - if (it == mActiveConfigsChangedReceivers.end()) { - return nullptr; - } else { - return it->second; - } -} - -void ConfigManager::Dump(FILE* out) { - lock_guard<mutex> lock(mMutex); - - fprintf(out, "CONFIGURATIONS\n"); - fprintf(out, " uid name\n"); - for (auto uidIt = mConfigs.cbegin(); uidIt != mConfigs.cend(); ++uidIt) { - for (auto it = uidIt->second.cbegin(); it != uidIt->second.cend(); ++it) { - fprintf(out, " %6d %lld\n", it->GetUid(), (long long)it->GetId()); - auto receiverIt = mConfigReceivers.find(*it); - if (receiverIt != mConfigReceivers.end()) { - fprintf(out, " -> received by PendingIntent as binder\n"); - } - } - } -} - -void ConfigManager::update_saved_configs_locked(const ConfigKey& key, - const vector<uint8_t>& buffer, - const int numBytes) { - // If there is a pre-existing config with same key we should first delete it. - remove_saved_configs(key); - - // Then we save the latest config. - string file_name = - StringPrintf("%s/%ld_%d_%lld", STATS_SERVICE_DIR, time(nullptr), - key.GetUid(), (long long)key.GetId()); - StorageManager::writeFile(file_name.c_str(), &buffer[0], numBytes); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h deleted file mode 100644 index bef057f96409..000000000000 --- a/cmds/statsd/src/config/ConfigManager.h +++ /dev/null @@ -1,181 +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. - */ - -#pragma once - -#include "config/ConfigKey.h" -#include "config/ConfigListener.h" - -#include <aidl/android/os/IPendingIntentRef.h> -#include <mutex> -#include <string> - -#include <stdio.h> - -using aidl::android::os::IPendingIntentRef; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -/** - * Keeps track of which configurations have been set from various sources. - */ -class ConfigManager : public virtual android::RefBase { -public: - ConfigManager(); - virtual ~ConfigManager(); - - /** - * Initialize ConfigListener by reading from disk and get updates. - */ - void Startup(); - - /* - * No-op initializer for tests. - */ - void StartupForTest(); - - /** - * Someone else wants to know about the configs. - */ - void AddListener(const sp<ConfigListener>& listener); - - /** - * A configuration was added or updated. - * - * Reports this to listeners. - */ - void UpdateConfig(const ConfigKey& key, const StatsdConfig& data); - - /** - * Sets the broadcast receiver for a configuration key. - */ - void SetConfigReceiver(const ConfigKey& key, const shared_ptr<IPendingIntentRef>& pir); - - /** - * Returns the package name and class name representing the broadcast receiver for this config. - */ - const shared_ptr<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const; - - /** - * Returns all config keys registered. - */ - std::vector<ConfigKey> GetAllConfigKeys() const; - - /** - * Erase any broadcast receiver associated with this config key. - */ - void RemoveConfigReceiver(const ConfigKey& key); - - /** - * Sets the broadcast receiver that is notified whenever the list of active configs - * changes for this uid. - */ - void SetActiveConfigsChangedReceiver(const int uid, const shared_ptr<IPendingIntentRef>& pir); - - /** - * Returns the broadcast receiver for active configs changed for this uid. - */ - - const shared_ptr<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const; - - /** - * Erase any active configs changed broadcast receiver associated with this uid. - */ - void RemoveActiveConfigsChangedReceiver(const int uid); - - /** - * A configuration was removed. - * - * Reports this to listeners. - */ - void RemoveConfig(const ConfigKey& key); - - /** - * Remove all of the configs for the given uid. - */ - void RemoveConfigs(int uid); - - /** - * Remove all of the configs from memory. - */ - void RemoveAllConfigs(); - - /** - * Text dump of our state for debugging. - */ - void Dump(FILE* out); - -private: - mutable std::mutex mMutex; - - /** - * Save the configs to disk. - */ - void update_saved_configs_locked(const ConfigKey& key, - const std::vector<uint8_t>& buffer, - const int numBytes); - - /** - * Remove saved configs from disk. - */ - void remove_saved_configs(const ConfigKey& key); - - /** - * Maps from uid to the config keys that have been set. - */ - std::map<int, std::set<ConfigKey>> mConfigs; - - /** - * Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef. - */ - std::map<ConfigKey, shared_ptr<IPendingIntentRef>> mConfigReceivers; - - /** - * Each uid can be subscribed by up to one receiver to notify that the list of active configs - * for this uid has changed. The receiver is specified as IPendingIntentRef. - */ - std::map<int, shared_ptr<IPendingIntentRef>> mActiveConfigsChangedReceivers; - - /** - * The ConfigListeners that will be told about changes. - */ - std::vector<sp<ConfigListener>> mListeners; - - // Death recipients that are triggered when the host process holding an - // 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 -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp deleted file mode 100644 index 85b660efc956..000000000000 --- a/cmds/statsd/src/external/Perfetto.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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. - */ - -#define DEBUG false // STOPSHIP if true -#include "config/ConfigKey.h" -#include "Log.h" - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert - -#include <android-base/unique_fd.h> -#include <inttypes.h> -#include <sys/wait.h> - -#include <string> - -namespace { -const char kDropboxTag[] = "perfetto"; -} - -namespace android { -namespace os { -namespace statsd { - -bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config, - int64_t subscription_id, - int64_t alert_id, - const ConfigKey& configKey) { - VLOG("Starting trace collection through perfetto"); - - if (!config.has_trace_config()) { - ALOGE("The perfetto trace config is empty, aborting"); - return false; - } - - char subscriptionId[25]; - char alertId[25]; - char configId[25]; - char configUid[25]; - snprintf(subscriptionId, sizeof(subscriptionId), "%" PRId64, subscription_id); - snprintf(alertId, sizeof(alertId), "%" PRId64, alert_id); - snprintf(configId, sizeof(configId), "%" PRId64, configKey.GetId()); - snprintf(configUid, sizeof(configUid), "%d", configKey.GetUid()); - - android::base::unique_fd readPipe; - android::base::unique_fd writePipe; - if (!android::base::Pipe(&readPipe, &writePipe)) { - ALOGE("pipe() failed while calling the Perfetto client: %s", strerror(errno)); - return false; - } - - pid_t pid = fork(); - if (pid < 0) { - ALOGE("fork() failed while calling the Perfetto client: %s", strerror(errno)); - return false; - } - - if (pid == 0) { - // Child process. - - // No malloc calls or library calls after this point. Remember that even - // ALOGx (aka android_printLog()) can use dynamic memory for vsprintf(). - - writePipe.reset(); // Close the write end (owned by the main process). - - // Replace stdin with |readPipe| so the main process can write into it. - if (dup2(readPipe.get(), STDIN_FILENO) < 0) _exit(1); - readPipe.reset(); - - // Replace stdout/stderr with /dev/null and close any other file - // descriptor. This is to avoid SELinux complaining about perfetto - // trying to access files accidentally left open by statsd (i.e. files - // that have been opened without the O_CLOEXEC flag). - int devNullFd = open("/dev/null", O_RDWR | O_CLOEXEC); - if (dup2(devNullFd, STDOUT_FILENO) < 0) _exit(2); - if (dup2(devNullFd, STDERR_FILENO) < 0) _exit(3); - close(devNullFd); - for (int i = 0; i < 1024; i++) { - if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) close(i); - } - - execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox", - kDropboxTag, "--alert-id", alertId, "--config-id", configId, "--config-uid", - configUid, "--subscription-id", subscriptionId, nullptr); - - // execl() doesn't return in case of success, if we get here something - // failed. - _exit(4); - } - - // Main process. - - readPipe.reset(); // Close the read end (owned by the child process). - - // Using fdopen() because fwrite() has the right logic to chunking write() - // over a pipe (see __sfvwrite()). - FILE* writePipeStream = android::base::Fdopen(std::move(writePipe), "wb"); - if (!writePipeStream) { - ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno)); - return false; - } - - const std::string& cfgProto = config.trace_config(); - size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream); - fclose(writePipeStream); - if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) { - ALOGE("fwrite() failed (ret: %zd) while calling the Perfetto client: %s", bytesWritten, - strerror(errno)); - return false; - } - - // This does NOT wait for the full duration of the trace. It just waits until - // the process has read the config from stdin and detached. - int childStatus = 0; - waitpid(pid, &childStatus, 0); - if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) { - ALOGE("Child process failed (0x%x) while calling the Perfetto client", childStatus); - return false; - } - - VLOG("CollectPerfettoTraceAndUploadToDropbox() succeeded"); - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h deleted file mode 100644 index 095782a49f9b..000000000000 --- a/cmds/statsd/src/external/Perfetto.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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. - */ - -#pragma once - -namespace android { -namespace os { -namespace statsd { - -class ConfigKey; -class PerfettoDetails; // Declared in statsd_config.pb.h - -// Starts the collection of a Perfetto trace with the given |config|. -// The trace is uploaded to Dropbox by the perfetto cmdline util once done. -// This method returns immediately after passing the config and does NOT wait -// for the full duration of the trace. -bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config, - int64_t subscription_id, - int64_t alert_id, - const ConfigKey& configKey); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/PullDataReceiver.h b/cmds/statsd/src/external/PullDataReceiver.h deleted file mode 100644 index dd5c0cfa04c1..000000000000 --- a/cmds/statsd/src/external/PullDataReceiver.h +++ /dev/null @@ -1,41 +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. - */ -#pragma once - -#include <utils/RefBase.h> -#include "StatsPuller.h" -#include "logd/LogEvent.h" - -namespace android { -namespace os { -namespace statsd { - -class PullDataReceiver : virtual public RefBase{ - public: - virtual ~PullDataReceiver() {} - /** - * @param data The pulled data. - * @param pullSuccess Whether the pull succeeded. If the pull does not succeed, the data for the - * bucket should be invalidated. - * @param originalPullTimeNs This is when all the pulls have been initiated (elapsed time). - */ - virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, - bool pullSuccess, int64_t originalPullTimeNs) = 0; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/PullResultReceiver.cpp b/cmds/statsd/src/external/PullResultReceiver.cpp deleted file mode 100644 index 8aa4792dc179..000000000000 --- a/cmds/statsd/src/external/PullResultReceiver.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "PullResultReceiver.h" - -namespace android { -namespace os { -namespace statsd { - -PullResultReceiver::PullResultReceiver( - std::function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCb) - : pullFinishCallback(std::move(pullFinishCb)) { -} - -Status PullResultReceiver::pullFinished(int32_t atomTag, bool success, - const vector<StatsEventParcel>& output) { - pullFinishCallback(atomTag, success, output); - return Status::ok(); -} - -PullResultReceiver::~PullResultReceiver() { -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/PullResultReceiver.h b/cmds/statsd/src/external/PullResultReceiver.h deleted file mode 100644 index ceaae801b2d5..000000000000 --- a/cmds/statsd/src/external/PullResultReceiver.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <aidl/android/os/BnPullAtomResultReceiver.h> -#include <aidl/android/util/StatsEventParcel.h> - -using namespace std; - -using Status = ::ndk::ScopedAStatus; -using aidl::android::os::BnPullAtomResultReceiver; -using aidl::android::util::StatsEventParcel; - -namespace android { -namespace os { -namespace statsd { - -class PullResultReceiver : public BnPullAtomResultReceiver { -public: - PullResultReceiver(function<void(int32_t, bool, const vector<StatsEventParcel>&)> - pullFinishCallback); - ~PullResultReceiver(); - - /** - * Binder call for finishing a pull. - */ - Status pullFinished(int32_t atomTag, bool success, - const vector<StatsEventParcel>& output) override; - -private: - function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCallback; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/PullUidProvider.h b/cmds/statsd/src/external/PullUidProvider.h deleted file mode 100644 index 2318c501ea4b..000000000000 --- a/cmds/statsd/src/external/PullUidProvider.h +++ /dev/null @@ -1,39 +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. - */ -#pragma once - -#include <utils/RefBase.h> - -#include "StatsPuller.h" -#include "logd/LogEvent.h" - -namespace android { -namespace os { -namespace statsd { - -class PullUidProvider : virtual public RefBase { -public: - virtual ~PullUidProvider() {} - - /** - * @param atomId The atom for which to get the uids. - */ - virtual vector<int32_t> getPullAtomUids(int32_t atomId) = 0; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp deleted file mode 100644 index 78e6f094db7e..000000000000 --- a/cmds/statsd/src/external/StatsCallbackPuller.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsCallbackPuller.h" -#include "PullResultReceiver.h" -#include "StatsPullerManager.h" -#include "logd/LogEvent.h" -#include "stats_log_util.h" - -#include <aidl/android/util/StatsEventParcel.h> - -using namespace std; - -using Status = ::ndk::ScopedAStatus; -using aidl::android::util::StatsEventParcel; -using ::ndk::SharedRefBase; - -namespace android { -namespace os { -namespace statsd { - -StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback, - const int64_t coolDownNs, int64_t timeoutNs, - const vector<int> additiveFields) - : StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) { - VLOG("StatsCallbackPuller created for tag %d", tagId); -} - -bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { - VLOG("StatsCallbackPuller called for tag %d", mTagId); - if(mCallback == nullptr) { - ALOGW("No callback registered"); - return false; - } - - // Shared variables needed in the result receiver. - shared_ptr<mutex> cv_mutex = make_shared<mutex>(); - shared_ptr<condition_variable> cv = make_shared<condition_variable>(); - shared_ptr<bool> pullFinish = make_shared<bool>(false); - shared_ptr<bool> pullSuccess = make_shared<bool>(false); - shared_ptr<vector<shared_ptr<LogEvent>>> sharedData = - make_shared<vector<shared_ptr<LogEvent>>>(); - - shared_ptr<PullResultReceiver> resultReceiver = SharedRefBase::make<PullResultReceiver>( - [cv_mutex, cv, pullFinish, pullSuccess, sharedData]( - int32_t atomTag, bool success, const vector<StatsEventParcel>& output) { - // This is the result of the pull, executing in a statsd binder thread. - // The pull could have taken a long time, and we should only modify - // data (the output param) if the pointer is in scope and the pull did not time out. - { - lock_guard<mutex> lk(*cv_mutex); - for (const StatsEventParcel& parcel: output) { - shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1); - bool valid = event->parseBuffer((uint8_t*)parcel.buffer.data(), - parcel.buffer.size()); - if (valid) { - sharedData->push_back(event); - } else { - StatsdStats::getInstance().noteAtomError(event->GetTagId(), - /*pull=*/true); - } - } - *pullSuccess = success; - *pullFinish = true; - } - cv->notify_one(); - }); - - // Initiate the pull. This is a oneway call to a different process, except - // in unit tests. In process calls are not oneway. - Status status = mCallback->onPullAtom(mTagId, resultReceiver); - if (!status.isOk()) { - StatsdStats::getInstance().notePullBinderCallFailed(mTagId); - return false; - } - - { - unique_lock<mutex> unique_lk(*cv_mutex); - // Wait until the pull finishes, or until the pull timeout. - cv->wait_for(unique_lk, chrono::nanoseconds(mPullTimeoutNs), - [pullFinish] { return *pullFinish; }); - if (!*pullFinish) { - // Note: The parent stats puller will also note that there was a timeout and that the - // cache should be cleared. Once we migrate all pullers to this callback, we could - // consolidate the logic. - return true; - } else { - // Only copy the data if we did not timeout and the pull was successful. - if (*pullSuccess) { - *data = std::move(*sharedData); - } - VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId); - return *pullSuccess; - } - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h deleted file mode 100644 index e82e8bb532be..000000000000 --- a/cmds/statsd/src/external/StatsCallbackPuller.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <aidl/android/os/IPullAtomCallback.h> -#include "StatsPuller.h" - -using aidl::android::os::IPullAtomCallback; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -class StatsCallbackPuller : public StatsPuller { -public: - explicit StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback, - const int64_t coolDownNs, const int64_t timeoutNs, - const std::vector<int> additiveFields); - -private: - bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override; - const shared_ptr<IPullAtomCallback> mCallback; - - FRIEND_TEST(StatsCallbackPullerTest, PullFail); - FRIEND_TEST(StatsCallbackPullerTest, PullSuccess); - FRIEND_TEST(StatsCallbackPullerTest, PullTimeout); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp deleted file mode 100644 index bb5d0a6bab58..000000000000 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ /dev/null @@ -1,126 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsPuller.h" -#include "StatsPullerManager.h" -#include "guardrail/StatsdStats.h" -#include "puller_util.h" -#include "stats_log_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::lock_guard; - -sp<UidMap> StatsPuller::mUidMap = nullptr; -void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; } - -StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_t pullTimeoutNs, - const std::vector<int> additiveFields) - : mTagId(tagId), - mPullTimeoutNs(pullTimeoutNs), - mCoolDownNs(coolDownNs), - mAdditiveFields(additiveFields), - mLastPullTimeNs(0), - mLastEventTimeNs(0) { -} - -bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) { - lock_guard<std::mutex> lock(mLock); - const int64_t elapsedTimeNs = getElapsedRealtimeNs(); - const int64_t systemUptimeMillis = getSystemUptimeMillis(); - StatsdStats::getInstance().notePull(mTagId); - const bool shouldUseCache = - (mLastEventTimeNs == eventTimeNs) || (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs); - if (shouldUseCache) { - if (mHasGoodData) { - (*data) = mCachedData; - StatsdStats::getInstance().notePullFromCache(mTagId); - - } - return mHasGoodData; - } - if (mLastPullTimeNs > 0) { - StatsdStats::getInstance().updateMinPullIntervalSec( - mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC); - } - mCachedData.clear(); - mLastPullTimeNs = elapsedTimeNs; - mLastEventTimeNs = eventTimeNs; - mHasGoodData = PullInternal(&mCachedData); - if (!mHasGoodData) { - return mHasGoodData; - } - const int64_t pullElapsedDurationNs = getElapsedRealtimeNs() - elapsedTimeNs; - const int64_t pullSystemUptimeDurationMillis = getSystemUptimeMillis() - systemUptimeMillis; - StatsdStats::getInstance().notePullTime(mTagId, pullElapsedDurationNs); - const bool pullTimeOut = pullElapsedDurationNs > mPullTimeoutNs; - if (pullTimeOut) { - // Something went wrong. Discard the data. - mCachedData.clear(); - mHasGoodData = false; - StatsdStats::getInstance().notePullTimeout( - mTagId, pullSystemUptimeDurationMillis, NanoToMillis(pullElapsedDurationNs)); - ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId, - (long long)pullElapsedDurationNs); - return mHasGoodData; - } - - if (mCachedData.size() > 0) { - mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId, mAdditiveFields); - } - - if (mCachedData.empty()) { - VLOG("Data pulled is empty"); - StatsdStats::getInstance().noteEmptyData(mTagId); - } - - (*data) = mCachedData; - return mHasGoodData; -} - -int StatsPuller::ForceClearCache() { - return clearCache(); -} - -int StatsPuller::clearCache() { - lock_guard<std::mutex> lock(mLock); - return clearCacheLocked(); -} - -int StatsPuller::clearCacheLocked() { - int ret = mCachedData.size(); - mCachedData.clear(); - mLastPullTimeNs = 0; - mLastEventTimeNs = 0; - return ret; -} - -int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) { - if (timestampNs - mLastPullTimeNs > mCoolDownNs) { - return clearCache(); - } else { - return 0; - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h deleted file mode 100644 index 470d15e6fbd1..000000000000 --- a/cmds/statsd/src/external/StatsPuller.h +++ /dev/null @@ -1,119 +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. - */ - -#pragma once - -#include <aidl/android/os/IStatsCompanionService.h> -#include <utils/RefBase.h> -#include <mutex> -#include <vector> -#include "packages/UidMap.h" - -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "puller_util.h" - -using aidl::android::os::IStatsCompanionService; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -class StatsPuller : public virtual RefBase { -public: - explicit StatsPuller(const int tagId, - const int64_t coolDownNs = NS_PER_SEC, - const int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs, - const std::vector<int> additiveFields = std::vector<int>()); - - virtual ~StatsPuller() {} - - // Pulls the most recent data. - // The data may be served from cache if consecutive pulls come within - // predefined cooldown time. - // Returns true if the pull was successful. - // Returns false when - // 1) the pull fails - // 2) pull takes longer than mPullTimeoutNs (intrinsic to puller) - // If a metric wants to make any change to the data, like timestamps, it - // should make a copy as this data may be shared with multiple metrics. - bool Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data); - - // Clear cache immediately - int ForceClearCache(); - - // Clear cache if elapsed time is more than cooldown time - int ClearCacheIfNecessary(int64_t timestampNs); - - static void SetUidMap(const sp<UidMap>& uidMap); - - virtual void SetStatsCompanionService( - shared_ptr<IStatsCompanionService> statsCompanionService) {}; - -protected: - const int mTagId; - - // Max time allowed to pull this atom. - // We cannot reliably kill a pull thread. So we don't terminate the puller. - // The data is discarded if the pull takes longer than this and mHasGoodData - // marked as false. - const int64_t mPullTimeoutNs = StatsdStats::kPullMaxDelayNs; - -private: - mutable std::mutex mLock; - - // Real puller impl. - virtual bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) = 0; - - bool mHasGoodData = false; - - // Minimum time before this puller does actual pull again. - // Pullers can cause significant impact to system health and battery. - // So that we don't pull too frequently. - // If a pull request comes before cooldown, a cached version from previous pull - // will be returned. - const int64_t mCoolDownNs = 1 * NS_PER_SEC; - - // The field numbers of the fields that need to be summed when merging - // isolated uid with host uid. - const std::vector<int> mAdditiveFields; - - int64_t mLastPullTimeNs; - - // All pulls happen due to an event (app upgrade, bucket boundary, condition change, etc). - // If multiple pulls need to be done at the same event time, we will always use the cache after - // the first pull. - int64_t mLastEventTimeNs; - - // Cache of data from last pull. If next request comes before cool down finishes, - // cached data will be returned. - // Cached data is cleared when - // 1) A pull fails - // 2) A new pull request comes after cooldown time. - // 3) clearCache is called. - std::vector<std::shared_ptr<LogEvent>> mCachedData; - - int clearCache(); - - int clearCacheLocked(); - - static sp<UidMap> mUidMap; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp deleted file mode 100644 index 8334b6b4db90..000000000000 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ /dev/null @@ -1,371 +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. - */ - -#define DEBUG false -#include "Log.h" - -#include "StatsPullerManager.h" - -#include <cutils/log.h> -#include <math.h> -#include <stdint.h> - -#include <algorithm> -#include <iostream> - -#include "../StatsService.h" -#include "../logd/LogEvent.h" -#include "../stats_log_util.h" -#include "../statscompanion_util.h" -#include "StatsCallbackPuller.h" -#include "TrainInfoPuller.h" -#include "statslog_statsd.h" - -using std::shared_ptr; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -// Stores the puller as a wp to avoid holding a reference in case it is unregistered and -// pullAtomCallbackDied is never called. -struct PullAtomCallbackDeathCookie { - PullAtomCallbackDeathCookie(const wp<StatsPullerManager>& pullerManager, - const PullerKey& pullerKey, const wp<StatsPuller>& puller) : - mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) { - } - - wp<StatsPullerManager> mPullerManager; - PullerKey mPullerKey; - wp<StatsPuller> mPuller; -}; - -void StatsPullerManager::pullAtomCallbackDied(void* cookie) { - PullAtomCallbackDeathCookie* cookie_ = static_cast<PullAtomCallbackDeathCookie*>(cookie); - sp<StatsPullerManager> thiz = cookie_->mPullerManager.promote(); - if (!thiz) { - return; - } - - const PullerKey& pullerKey = cookie_->mPullerKey; - wp<StatsPuller> puller = cookie_->mPuller; - - // Erase the mapping from the puller key to the puller if the mapping still exists. - // Note that we are removing the StatsPuller object, which internally holds the binder - // IPullAtomCallback. However, each new registration creates a new StatsPuller, so this works. - lock_guard<mutex> lock(thiz->mLock); - const auto& it = thiz->kAllPullAtomInfo.find(pullerKey); - if (it != thiz->kAllPullAtomInfo.end() && puller != nullptr && puller == it->second) { - StatsdStats::getInstance().notePullerCallbackRegistrationChanged(pullerKey.atomTag, - /*registered=*/false); - thiz->kAllPullAtomInfo.erase(pullerKey); - } - // The death recipient corresponding to this specific IPullAtomCallback can never - // be triggered again, so free up resources. - delete cookie_; -} - -// Values smaller than this may require to update the alarm. -const int64_t NO_ALARM_UPDATE = INT64_MAX; - -StatsPullerManager::StatsPullerManager() - : kAllPullAtomInfo({ - // TrainInfo. - {{.atomTag = util::TRAIN_INFO, .uid = AID_STATSD}, new TrainInfoPuller()}, - }), - mNextPullTimeNs(NO_ALARM_UPDATE), - mPullAtomCallbackDeathRecipient(AIBinder_DeathRecipient_new(pullAtomCallbackDied)) { -} - -bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs, - vector<shared_ptr<LogEvent>>* data) { - std::lock_guard<std::mutex> _l(mLock); - return PullLocked(tagId, configKey, eventTimeNs, data); -} - -bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - std::lock_guard<std::mutex> _l(mLock); - return PullLocked(tagId, uids, eventTimeNs, data); -} - -bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey, - const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) { - vector<int32_t> uids; - const auto& uidProviderIt = mPullUidProviders.find(configKey); - if (uidProviderIt == mPullUidProviders.end()) { - ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId, - configKey.ToString().c_str()); - StatsdStats::getInstance().notePullUidProviderNotFound(tagId); - return false; - } - sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote(); - if (pullUidProvider == nullptr) { - ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId, - configKey.ToString().c_str()); - StatsdStats::getInstance().notePullUidProviderNotFound(tagId); - return false; - } - uids = pullUidProvider->getPullAtomUids(tagId); - return PullLocked(tagId, uids, eventTimeNs, data); -} - -bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids, - const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) { - VLOG("Initiating pulling %d", tagId); - for (int32_t uid : uids) { - PullerKey key = {.atomTag = tagId, .uid = uid}; - auto pullerIt = kAllPullAtomInfo.find(key); - if (pullerIt != kAllPullAtomInfo.end()) { - bool ret = pullerIt->second->Pull(eventTimeNs, data); - VLOG("pulled %zu items", data->size()); - if (!ret) { - StatsdStats::getInstance().notePullFailed(tagId); - } - return ret; - } - } - StatsdStats::getInstance().notePullerNotFound(tagId); - ALOGW("StatsPullerManager: Unknown tagId %d", tagId); - return false; // Return early since we don't know what to pull. -} - -bool StatsPullerManager::PullerForMatcherExists(int tagId) const { - // Pulled atoms might be registered after we parse the config, so just make sure the id is in - // an appropriate range. - return isVendorPulledAtom(tagId) || isPulledAtom(tagId); -} - -void StatsPullerManager::updateAlarmLocked() { - if (mNextPullTimeNs == NO_ALARM_UPDATE) { - VLOG("No need to set alarms. Skipping"); - return; - } - - // 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."); - } - return; -} - -void StatsPullerManager::SetStatsCompanionService( - shared_ptr<IStatsCompanionService> statsCompanionService) { - std::lock_guard<std::mutex> _l(mLock); - shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService; - mStatsCompanionService = statsCompanionService; - for (const auto& pulledAtom : kAllPullAtomInfo) { - pulledAtom.second->SetStatsCompanionService(statsCompanionService); - } - if (mStatsCompanionService != nullptr) { - updateAlarmLocked(); - } -} - -void StatsPullerManager::RegisterReceiver(int tagId, const ConfigKey& configKey, - wp<PullDataReceiver> receiver, int64_t nextPullTimeNs, - int64_t intervalNs) { - std::lock_guard<std::mutex> _l(mLock); - auto& receivers = mReceivers[{.atomTag = tagId, .configKey = configKey}]; - for (auto it = receivers.begin(); it != receivers.end(); it++) { - if (it->receiver == receiver) { - VLOG("Receiver already registered of %d", (int)receivers.size()); - return; - } - } - ReceiverInfo receiverInfo; - receiverInfo.receiver = receiver; - - // Round it to the nearest minutes. This is the limit of alarm manager. - // In practice, we should always have larger buckets. - int64_t roundedIntervalNs = intervalNs / NS_PER_SEC / 60 * NS_PER_SEC * 60; - // Scheduled pulling should be at least 1 min apart. - // This can be lower in cts tests, in which case we round it to 1 min. - if (roundedIntervalNs < 60 * (int64_t)NS_PER_SEC) { - roundedIntervalNs = 60 * (int64_t)NS_PER_SEC; - } - - receiverInfo.intervalNs = roundedIntervalNs; - receiverInfo.nextPullTimeNs = nextPullTimeNs; - receivers.push_back(receiverInfo); - - // There is only one alarm for all pulled events. So only set it to the smallest denom. - if (nextPullTimeNs < mNextPullTimeNs) { - VLOG("Updating next pull time %lld", (long long)mNextPullTimeNs); - mNextPullTimeNs = nextPullTimeNs; - updateAlarmLocked(); - } - VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size()); -} - -void StatsPullerManager::UnRegisterReceiver(int tagId, const ConfigKey& configKey, - wp<PullDataReceiver> receiver) { - std::lock_guard<std::mutex> _l(mLock); - auto receiversIt = mReceivers.find({.atomTag = tagId, .configKey = configKey}); - if (receiversIt == mReceivers.end()) { - VLOG("Unknown pull code or no receivers: %d", tagId); - return; - } - std::list<ReceiverInfo>& receivers = receiversIt->second; - for (auto it = receivers.begin(); it != receivers.end(); it++) { - if (receiver == it->receiver) { - receivers.erase(it); - VLOG("Puller for tagId %d unregistered of %d", tagId, (int)receivers.size()); - return; - } - } -} - -void StatsPullerManager::RegisterPullUidProvider(const ConfigKey& configKey, - wp<PullUidProvider> provider) { - std::lock_guard<std::mutex> _l(mLock); - mPullUidProviders[configKey] = provider; -} - -void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey, - wp<PullUidProvider> provider) { - std::lock_guard<std::mutex> _l(mLock); - const auto& it = mPullUidProviders.find(configKey); - if (it != mPullUidProviders.end() && it->second == provider) { - mPullUidProviders.erase(it); - } -} - -void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) { - std::lock_guard<std::mutex> _l(mLock); - int64_t wallClockNs = getWallClockNs(); - - int64_t minNextPullTimeNs = NO_ALARM_UPDATE; - - vector<pair<const ReceiverKey*, vector<ReceiverInfo*>>> needToPull; - for (auto& pair : mReceivers) { - vector<ReceiverInfo*> receivers; - if (pair.second.size() != 0) { - for (ReceiverInfo& receiverInfo : pair.second) { - if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) { - receivers.push_back(&receiverInfo); - } else { - if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) { - minNextPullTimeNs = receiverInfo.nextPullTimeNs; - } - } - } - if (receivers.size() > 0) { - needToPull.push_back(make_pair(&pair.first, receivers)); - } - } - } - for (const auto& pullInfo : needToPull) { - vector<shared_ptr<LogEvent>> data; - bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey, - elapsedTimeNs, &data); - if (!pullSuccess) { - VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs); - } - - // Convention is to mark pull atom timestamp at request time. - // If we pull at t0, puller starts at t1, finishes at t2, and send back - // at t3, we mark t0 as its timestamp, which should correspond to its - // triggering event, such as condition change at t0. - // Here the triggering event is alarm fired from AlarmManager. - // In ValueMetricProducer and GaugeMetricProducer we do same thing - // when pull on condition change, etc. - for (auto& event : data) { - event->setElapsedTimestampNs(elapsedTimeNs); - event->setLogdWallClockTimestampNs(wallClockNs); - } - - for (const auto& receiverInfo : pullInfo.second) { - sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote(); - if (receiverPtr != nullptr) { - receiverPtr->onDataPulled(data, pullSuccess, elapsedTimeNs); - // We may have just come out of a coma, compute next pull time. - int numBucketsAhead = - (elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs; - receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs; - if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) { - minNextPullTimeNs = receiverInfo->nextPullTimeNs; - } - } else { - VLOG("receiver already gone."); - } - } - } - - VLOG("mNextPullTimeNs: %lld updated to %lld", (long long)mNextPullTimeNs, - (long long)minNextPullTimeNs); - mNextPullTimeNs = minNextPullTimeNs; - updateAlarmLocked(); -} - -int StatsPullerManager::ForceClearPullerCache() { - std::lock_guard<std::mutex> _l(mLock); - int totalCleared = 0; - for (const auto& pulledAtom : kAllPullAtomInfo) { - totalCleared += pulledAtom.second->ForceClearCache(); - } - return totalCleared; -} - -int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) { - std::lock_guard<std::mutex> _l(mLock); - int totalCleared = 0; - for (const auto& pulledAtom : kAllPullAtomInfo) { - totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs); - } - return totalCleared; -} - -void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag, - const int64_t coolDownNs, const int64_t timeoutNs, - const vector<int32_t>& additiveFields, - const shared_ptr<IPullAtomCallback>& callback) { - std::lock_guard<std::mutex> _l(mLock); - VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag); - - if (callback == nullptr) { - ALOGW("SetPullAtomCallback called with null callback for atom %d.", atomTag); - return; - } - - StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true); - int64_t actualCoolDownNs = coolDownNs < kMinCoolDownNs ? kMinCoolDownNs : coolDownNs; - int64_t actualTimeoutNs = timeoutNs > kMaxTimeoutNs ? kMaxTimeoutNs : timeoutNs; - - sp<StatsCallbackPuller> puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs, - actualTimeoutNs, additiveFields); - PullerKey key = {.atomTag = atomTag, .uid = uid}; - AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(), - new PullAtomCallbackDeathCookie(this, key, puller)); - kAllPullAtomInfo[key] = puller; -} - -void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) { - std::lock_guard<std::mutex> _l(mLock); - PullerKey key = {.atomTag = atomTag, .uid = uid}; - if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) { - StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, - /*registered=*/false); - kAllPullAtomInfo.erase(key); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h deleted file mode 100644 index 489cbdbe5400..000000000000 --- a/cmds/statsd/src/external/StatsPullerManager.h +++ /dev/null @@ -1,189 +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. - */ - -#pragma once - -#include <aidl/android/os/IPullAtomCallback.h> -#include <aidl/android/os/IStatsCompanionService.h> -#include <utils/RefBase.h> - -#include <list> -#include <vector> - -#include "PullDataReceiver.h" -#include "PullUidProvider.h" -#include "StatsPuller.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "packages/UidMap.h" - -using aidl::android::os::IPullAtomCallback; -using aidl::android::os::IStatsCompanionService; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -typedef struct PullerKey { - // The uid of the process that registers this puller. - const int uid = -1; - // The atom that this puller is for. - const int atomTag; - - bool operator<(const PullerKey& that) const { - if (uid < that.uid) { - return true; - } - if (uid > that.uid) { - return false; - } - return atomTag < that.atomTag; - }; - - bool operator==(const PullerKey& that) const { - return uid == that.uid && atomTag == that.atomTag; - }; -} PullerKey; - -class StatsPullerManager : public virtual RefBase { -public: - StatsPullerManager(); - - virtual ~StatsPullerManager() { - } - - - // Registers a receiver for tagId. It will be pulled on the nextPullTimeNs - // and then every intervalNs thereafter. - virtual void RegisterReceiver(int tagId, const ConfigKey& configKey, - wp<PullDataReceiver> receiver, int64_t nextPullTimeNs, - int64_t intervalNs); - - // Stop listening on a tagId. - virtual void UnRegisterReceiver(int tagId, const ConfigKey& configKey, - wp<PullDataReceiver> receiver); - - // Registers a pull uid provider for the config key. When pulling atoms, it will be used to - // determine which uids to pull from. - virtual void RegisterPullUidProvider(const ConfigKey& configKey, wp<PullUidProvider> provider); - - // Unregister a pull uid provider. - virtual void UnregisterPullUidProvider(const ConfigKey& configKey, - wp<PullUidProvider> provider); - - // Verify if we know how to pull for this matcher - bool PullerForMatcherExists(int tagId) const; - - void OnAlarmFired(int64_t elapsedTimeNs); - - // Pulls the most recent data. - // The data may be served from cache if consecutive pulls come within - // mCoolDownNs. - // Returns true if the pull was successful. - // Returns false when - // 1) the pull fails - // 2) pull takes longer than mPullTimeoutNs (intrinsic to puller) - // 3) Either a PullUidProvider was not registered for the config, or the there was no puller - // registered for any of the uids for this atom. - // If the metric wants to make any change to the data, like timestamps, they - // should make a copy as this data may be shared with multiple metrics. - virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data); - - // Same as above, but directly specify the allowed uids to pull from. - virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data); - - // Clear pull data cache immediately. - int ForceClearPullerCache(); - - // Clear pull data cache if it is beyond respective cool down time. - int ClearPullerCacheIfNecessary(int64_t timestampNs); - - void SetStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService); - - void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs, - const int64_t timeoutNs, const vector<int32_t>& additiveFields, - const shared_ptr<IPullAtomCallback>& callback); - - void UnregisterPullAtomCallback(const int uid, const int32_t atomTag); - - std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo; - -private: - const static int64_t kMinCoolDownNs = NS_PER_SEC; - const static int64_t kMaxTimeoutNs = 10 * NS_PER_SEC; - shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr; - - // A struct containing an atom id and a Config Key - typedef struct ReceiverKey { - const int atomTag; - const ConfigKey configKey; - - inline bool operator<(const ReceiverKey& that) const { - return atomTag == that.atomTag ? configKey < that.configKey : atomTag < that.atomTag; - } - } ReceiverKey; - - typedef struct { - int64_t nextPullTimeNs; - int64_t intervalNs; - wp<PullDataReceiver> receiver; - } ReceiverInfo; - - // mapping from Receiver Key to receivers - std::map<ReceiverKey, std::list<ReceiverInfo>> mReceivers; - - // mapping from Config Key to the PullUidProvider for that config - std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders; - - bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data); - - bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data); - - // locks for data receiver and StatsCompanionService changes - std::mutex mLock; - - void updateAlarmLocked(); - - int64_t mNextPullTimeNs; - - // Death recipient that is triggered when the process holding the IPullAtomCallback has died. - ::ndk::ScopedAIBinder_DeathRecipient mPullAtomCallbackDeathRecipient; - - /** - * Death recipient callback that is called when a pull atom callback dies. - * The cookie is a pointer to a PullAtomCallbackDeathCookie. - */ - static void pullAtomCallbackDied(void* cookie); - - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); - - FRIEND_TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp deleted file mode 100644 index 3837f4a1a517..000000000000 --- a/cmds/statsd/src/external/TrainInfoPuller.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "external/StatsPuller.h" - -#include "TrainInfoPuller.h" -#include "logd/LogEvent.h" -#include "stats_log_util.h" -#include "statslog_statsd.h" -#include "storage/StorageManager.h" - -using std::make_shared; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -TrainInfoPuller::TrainInfoPuller() : - StatsPuller(util::TRAIN_INFO) { -} - -bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { - vector<InstallTrainInfo> trainInfoList = - StorageManager::readAllTrainInfo(); - if (trainInfoList.empty()) { - ALOGW("Train info was empty."); - return true; - } - for (InstallTrainInfo& trainInfo : trainInfoList) { - auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo); - data->push_back(event); - } - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/TrainInfoPuller.h b/cmds/statsd/src/external/TrainInfoPuller.h deleted file mode 100644 index 615d02351fd3..000000000000 --- a/cmds/statsd/src/external/TrainInfoPuller.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "StatsPuller.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * Reads train info from disk. - */ -class TrainInfoPuller : public StatsPuller { - public: - TrainInfoPuller(); - - private: - bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp deleted file mode 100644 index aa99d0082bdd..000000000000 --- a/cmds/statsd/src/external/puller_util.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "puller_util.h" - -namespace android { -namespace os { -namespace statsd { - -using namespace std; - -/** - * Process all data and merge isolated with host if necessary. - * For example: - * NetworkBytesAtom { - * int uid = 1; - * State process_state = 2; - * int byte_send = 3; - * int byte_recv = 4; - * } - * additive fields are {3, 4} - * If we pulled the following events (uid1_child is an isolated uid which maps to uid1): - * [uid1, fg, 100, 200] - * [uid1_child, fg, 100, 200] - * [uid1, bg, 100, 200] - * - * We want to merge them and results should be: - * [uid1, fg, 200, 400] - * [uid1, bg, 100, 200] - * - * All atoms should be of the same tagId. All fields should be present. - */ -void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap, - int tagId, const vector<int>& additiveFieldsVec) { - // Check the first LogEvent for attribution chain or a uid field as either all atoms with this - // tagId have them or none of them do. - std::pair<int, int> attrIndexRange; - const bool hasAttributionChain = data[0]->hasAttributionChain(&attrIndexRange); - bool hasUidField = (data[0]->getUidFieldIndex() != -1); - - if (!hasAttributionChain && !hasUidField) { - VLOG("No uid or attribution chain to merge, atom %d", tagId); - return; - } - - // 1. Map all isolated uid in-place to host uid - for (shared_ptr<LogEvent>& event : data) { - if (event->GetTagId() != tagId) { - ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId()); - return; - } - if (hasAttributionChain) { - vector<FieldValue>* const fieldValues = event->getMutableValues(); - for (int i = attrIndexRange.first; i <= attrIndexRange.second; i++) { - FieldValue& fieldValue = fieldValues->at(i); - if (isAttributionUidField(fieldValue)) { - const int hostUid = uidMap->getHostUidOrSelf(fieldValue.mValue.int_value); - fieldValue.mValue.setInt(hostUid); - } - } - } else { - int uidFieldIndex = event->getUidFieldIndex(); - if (uidFieldIndex != -1) { - Value& value = (*event->getMutableValues())[uidFieldIndex].mValue; - const int hostUid = uidMap->getHostUidOrSelf(value.int_value); - value.setInt(hostUid); - } else { - ALOGE("Malformed log, uid not found. %s", event->ToString().c_str()); - } - } - } - - // 2. sort the data, bit-wise - sort(data.begin(), data.end(), - [](const shared_ptr<LogEvent>& lhs, const shared_ptr<LogEvent>& rhs) { - if (lhs->size() != rhs->size()) { - return lhs->size() < rhs->size(); - } - const std::vector<FieldValue>& lhsValues = lhs->getValues(); - const std::vector<FieldValue>& rhsValues = rhs->getValues(); - for (int i = 0; i < (int)lhs->size(); i++) { - if (lhsValues[i] != rhsValues[i]) { - return lhsValues[i] < rhsValues[i]; - } - } - return false; - }); - - vector<shared_ptr<LogEvent>> mergedData; - const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end()); - bool needMerge = true; - - // 3. do the merge. - // The loop invariant is this: for every event, check if it differs on - // non-additive fields, or have different attribution chain length. - // If so, no need to merge, add itself to the result. - // Otherwise, merge the value onto the one immediately next to it. - for (int i = 0; i < (int)data.size() - 1; i++) { - // Size different, must be different chains. - if (data[i]->size() != data[i + 1]->size()) { - mergedData.push_back(data[i]); - continue; - } - vector<FieldValue>* lhsValues = data[i]->getMutableValues(); - vector<FieldValue>* rhsValues = data[i + 1]->getMutableValues(); - needMerge = true; - for (int p = 0; p < (int)lhsValues->size(); p++) { - if ((*lhsValues)[p] != (*rhsValues)[p]) { - int pos = (*lhsValues)[p].mField.getPosAtDepth(0); - // Differ on non-additive field, abort. - if (additiveFields.find(pos) == additiveFields.end()) { - needMerge = false; - break; - } - } - } - if (!needMerge) { - mergedData.push_back(data[i]); - continue; - } - // This should be infrequent operation. - for (int p = 0; p < (int)lhsValues->size(); p++) { - int pos = (*lhsValues)[p].mField.getPosAtDepth(0); - if (additiveFields.find(pos) != additiveFields.end()) { - (*rhsValues)[p].mValue += (*lhsValues)[p].mValue; - } - } - } - mergedData.push_back(data.back()); - - data.clear(); - data = mergedData; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h deleted file mode 100644 index afcf68ca10ad..000000000000 --- a/cmds/statsd/src/external/puller_util.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include <vector> -#include "StatsPuller.h" -#include "logd/LogEvent.h" -#include "packages/UidMap.h" - -namespace android { -namespace os { -namespace statsd { - -void mapAndMergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data, - const sp<UidMap>& uidMap, int tagId, - const vector<int>& additiveFieldsVec); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp deleted file mode 100644 index 6e89038f4152..000000000000 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ /dev/null @@ -1,1101 +0,0 @@ -/* - * 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 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. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsdStats.h" - -#include <android/util/ProtoOutputStream.h> -#include "../stats_log_util.h" -#include "statslog_statsd.h" -#include "storage/StorageManager.h" - -namespace android { -namespace os { -namespace statsd { - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::lock_guard; -using std::shared_ptr; -using std::string; -using std::to_string; -using std::vector; - -const int FIELD_ID_BEGIN_TIME = 1; -const int FIELD_ID_END_TIME = 2; -const int FIELD_ID_CONFIG_STATS = 3; -const int FIELD_ID_ATOM_STATS = 7; -const int FIELD_ID_UIDMAP_STATS = 8; -const int FIELD_ID_ANOMALY_ALARM_STATS = 9; -const int FIELD_ID_PERIODIC_ALARM_STATS = 12; -const int FIELD_ID_SYSTEM_SERVER_RESTART = 15; -const int FIELD_ID_LOGGER_ERROR_STATS = 16; -const int FIELD_ID_OVERFLOW = 18; -const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL = 19; - -const int FIELD_ID_ATOM_STATS_TAG = 1; -const int FIELD_ID_ATOM_STATS_COUNT = 2; -const int FIELD_ID_ATOM_STATS_ERROR_COUNT = 3; - -const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1; -const int FIELD_ID_PERIODIC_ALARMS_REGISTERED = 1; - -const int FIELD_ID_LOG_LOSS_STATS_TIME = 1; -const int FIELD_ID_LOG_LOSS_STATS_COUNT = 2; -const int FIELD_ID_LOG_LOSS_STATS_ERROR = 3; -const int FIELD_ID_LOG_LOSS_STATS_TAG = 4; -const int FIELD_ID_LOG_LOSS_STATS_UID = 5; -const int FIELD_ID_LOG_LOSS_STATS_PID = 6; - -const int FIELD_ID_OVERFLOW_COUNT = 1; -const int FIELD_ID_OVERFLOW_MAX_HISTORY = 2; -const int FIELD_ID_OVERFLOW_MIN_HISTORY = 3; - -const int FIELD_ID_CONFIG_STATS_UID = 1; -const int FIELD_ID_CONFIG_STATS_ID = 2; -const int FIELD_ID_CONFIG_STATS_CREATION = 3; -const int FIELD_ID_CONFIG_STATS_RESET = 19; -const int FIELD_ID_CONFIG_STATS_DELETION = 4; -const int FIELD_ID_CONFIG_STATS_METRIC_COUNT = 5; -const int FIELD_ID_CONFIG_STATS_CONDITION_COUNT = 6; -const int FIELD_ID_CONFIG_STATS_MATCHER_COUNT = 7; -const int FIELD_ID_CONFIG_STATS_ALERT_COUNT = 8; -const int FIELD_ID_CONFIG_STATS_VALID = 9; -const int FIELD_ID_CONFIG_STATS_BROADCAST = 10; -const int FIELD_ID_CONFIG_STATS_DATA_DROP_TIME = 11; -const int FIELD_ID_CONFIG_STATS_DATA_DROP_BYTES = 21; -const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME = 12; -const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES = 20; -const int FIELD_ID_CONFIG_STATS_MATCHER_STATS = 13; -const int FIELD_ID_CONFIG_STATS_CONDITION_STATS = 14; -const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15; -const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16; -const int FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS = 17; -const int FIELD_ID_CONFIG_STATS_ANNOTATION = 18; -const int FIELD_ID_CONFIG_STATS_ACTIVATION = 22; -const int FIELD_ID_CONFIG_STATS_DEACTIVATION = 23; -const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT64 = 1; -const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT32 = 2; - -const int FIELD_ID_MATCHER_STATS_ID = 1; -const int FIELD_ID_MATCHER_STATS_COUNT = 2; -const int FIELD_ID_CONDITION_STATS_ID = 1; -const int FIELD_ID_CONDITION_STATS_COUNT = 2; -const int FIELD_ID_METRIC_STATS_ID = 1; -const int FIELD_ID_METRIC_STATS_COUNT = 2; -const int FIELD_ID_ALERT_STATS_ID = 1; -const int FIELD_ID_ALERT_STATS_COUNT = 2; - -const int FIELD_ID_UID_MAP_CHANGES = 1; -const int FIELD_ID_UID_MAP_BYTES_USED = 2; -const int FIELD_ID_UID_MAP_DROPPED_CHANGES = 3; -const int FIELD_ID_UID_MAP_DELETED_APPS = 4; - -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 = { - {util::BINDER_CALLS, {6000, 10000}}, - {util::LOOPER_STATS, {1500, 2500}}, - {util::CPU_TIME_PER_UID_FREQ, {6000, 10000}}, -}; - -StatsdStats::StatsdStats() { - mPushedAtomStats.resize(kMaxPushedAtomId + 1); - mStartTimeSec = getWallClockSec(); -} - -StatsdStats& StatsdStats::getInstance() { - static StatsdStats statsInstance; - return statsInstance; -} - -void StatsdStats::addToIceBoxLocked(shared_ptr<ConfigStats>& stats) { - // The size of mIceBox grows strictly by one at a time. It won't be > kMaxIceBoxSize. - if (mIceBox.size() == kMaxIceBoxSize) { - mIceBox.pop_front(); - } - mIceBox.push_back(stats); -} - -void StatsdStats::noteConfigReceived( - const ConfigKey& key, int metricsCount, int conditionsCount, int matchersCount, - int alertsCount, const std::list<std::pair<const int64_t, const int32_t>>& annotations, - bool isValid) { - lock_guard<std::mutex> lock(mLock); - int32_t nowTimeSec = getWallClockSec(); - - // If there is an existing config for the same key, icebox the old config. - noteConfigRemovedInternalLocked(key); - - shared_ptr<ConfigStats> configStats = std::make_shared<ConfigStats>(); - configStats->uid = key.GetUid(); - configStats->id = key.GetId(); - configStats->creation_time_sec = nowTimeSec; - configStats->metric_count = metricsCount; - configStats->condition_count = conditionsCount; - configStats->matcher_count = matchersCount; - configStats->alert_count = alertsCount; - configStats->is_valid = isValid; - for (auto& v : annotations) { - configStats->annotations.emplace_back(v); - } - - if (isValid) { - mConfigStats[key] = configStats; - } else { - configStats->deletion_time_sec = nowTimeSec; - addToIceBoxLocked(configStats); - } -} - -void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) { - auto it = mConfigStats.find(key); - if (it != mConfigStats.end()) { - int32_t nowTimeSec = getWallClockSec(); - it->second->deletion_time_sec = nowTimeSec; - addToIceBoxLocked(it->second); - mConfigStats.erase(it); - } -} - -void StatsdStats::noteConfigRemoved(const ConfigKey& key) { - lock_guard<std::mutex> lock(mLock); - noteConfigRemovedInternalLocked(key); -} - -void StatsdStats::noteConfigResetInternalLocked(const ConfigKey& key) { - auto it = mConfigStats.find(key); - if (it != mConfigStats.end()) { - it->second->reset_time_sec = getWallClockSec(); - } -} - -void StatsdStats::noteConfigReset(const ConfigKey& key) { - lock_guard<std::mutex> lock(mLock); - noteConfigResetInternalLocked(key); -} - -void StatsdStats::noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError, - int32_t lastTag, int32_t uid, int32_t pid) { - lock_guard<std::mutex> lock(mLock); - if (mLogLossStats.size() == kMaxLoggerErrors) { - mLogLossStats.pop_front(); - } - mLogLossStats.emplace_back(wallClockTimeSec, count, lastError, lastTag, uid, pid); -} - -void StatsdStats::noteBroadcastSent(const ConfigKey& key) { - noteBroadcastSent(key, getWallClockSec()); -} - -void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { - lock_guard<std::mutex> lock(mLock); - auto it = mConfigStats.find(key); - if (it == mConfigStats.end()) { - ALOGE("Config key %s not found!", key.ToString().c_str()); - return; - } - if (it->second->broadcast_sent_time_sec.size() == kMaxTimestampCount) { - it->second->broadcast_sent_time_sec.pop_front(); - } - it->second->broadcast_sent_time_sec.push_back(timeSec); -} - -void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated) { - noteActiveStatusChanged(key, activated, getWallClockSec()); -} - -void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated, int32_t timeSec) { - lock_guard<std::mutex> lock(mLock); - auto it = mConfigStats.find(key); - if (it == mConfigStats.end()) { - ALOGE("Config key %s not found!", key.ToString().c_str()); - return; - } - auto& vec = activated ? it->second->activation_time_sec - : it->second->deactivation_time_sec; - if (vec.size() == kMaxTimestampCount) { - vec.pop_front(); - } - vec.push_back(timeSec); -} - -void StatsdStats::noteActivationBroadcastGuardrailHit(const int uid) { - noteActivationBroadcastGuardrailHit(uid, getWallClockSec()); -} - -void StatsdStats::noteActivationBroadcastGuardrailHit(const int uid, const int32_t timeSec) { - lock_guard<std::mutex> lock(mLock); - auto& guardrailTimes = mActivationBroadcastGuardrailStats[uid]; - if (guardrailTimes.size() == kMaxTimestampCount) { - guardrailTimes.pop_front(); - } - guardrailTimes.push_back(timeSec); -} - -void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes) { - noteDataDropped(key, totalBytes, getWallClockSec()); -} - -void StatsdStats::noteEventQueueOverflow(int64_t oldestEventTimestampNs) { - lock_guard<std::mutex> lock(mLock); - - mOverflowCount++; - - int64_t history = getElapsedRealtimeNs() - oldestEventTimestampNs; - - if (history > mMaxQueueHistoryNs) { - mMaxQueueHistoryNs = history; - } - - if (history < mMinQueueHistoryNs) { - mMinQueueHistoryNs = history; - } -} - -void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec) { - lock_guard<std::mutex> lock(mLock); - auto it = mConfigStats.find(key); - if (it == mConfigStats.end()) { - ALOGE("Config key %s not found!", key.ToString().c_str()); - return; - } - if (it->second->data_drop_time_sec.size() == kMaxTimestampCount) { - it->second->data_drop_time_sec.pop_front(); - it->second->data_drop_bytes.pop_front(); - } - it->second->data_drop_time_sec.push_back(timeSec); - it->second->data_drop_bytes.push_back(totalBytes); -} - -void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes) { - noteMetricsReportSent(key, num_bytes, getWallClockSec()); -} - -void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes, - int32_t timeSec) { - lock_guard<std::mutex> lock(mLock); - auto it = mConfigStats.find(key); - if (it == mConfigStats.end()) { - ALOGE("Config key %s not found!", key.ToString().c_str()); - return; - } - if (it->second->dump_report_stats.size() == kMaxTimestampCount) { - it->second->dump_report_stats.pop_front(); - } - it->second->dump_report_stats.push_back(std::make_pair(timeSec, num_bytes)); -} - -void StatsdStats::noteUidMapDropped(int deltas) { - lock_guard<std::mutex> lock(mLock); - mUidMapStats.dropped_changes += mUidMapStats.dropped_changes + deltas; -} - -void StatsdStats::noteUidMapAppDeletionDropped() { - lock_guard<std::mutex> lock(mLock); - mUidMapStats.deleted_apps++; -} - -void StatsdStats::setUidMapChanges(int changes) { - lock_guard<std::mutex> lock(mLock); - mUidMapStats.changes = changes; -} - -void StatsdStats::setCurrentUidMapMemory(int bytes) { - lock_guard<std::mutex> lock(mLock); - mUidMapStats.bytes_used = bytes; -} - -void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) { - lock_guard<std::mutex> lock(mLock); - // if name doesn't exist before, it will create the key with count 0. - auto statsIt = mConfigStats.find(key); - if (statsIt == mConfigStats.end()) { - return; - } - - auto& conditionSizeMap = statsIt->second->condition_stats; - if (size > conditionSizeMap[id]) { - conditionSizeMap[id] = size; - } -} - -void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) { - lock_guard<std::mutex> lock(mLock); - // if name doesn't exist before, it will create the key with count 0. - auto statsIt = mConfigStats.find(key); - if (statsIt == mConfigStats.end()) { - return; - } - auto& metricsDimensionMap = statsIt->second->metric_stats; - if (size > metricsDimensionMap[id]) { - metricsDimensionMap[id] = size; - } -} - -void StatsdStats::noteMetricDimensionInConditionSize( - const ConfigKey& key, const int64_t& id, int size) { - lock_guard<std::mutex> lock(mLock); - // if name doesn't exist before, it will create the key with count 0. - auto statsIt = mConfigStats.find(key); - if (statsIt == mConfigStats.end()) { - return; - } - auto& metricsDimensionMap = statsIt->second->metric_dimension_in_condition_stats; - if (size > metricsDimensionMap[id]) { - metricsDimensionMap[id] = size; - } -} - -void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) { - lock_guard<std::mutex> lock(mLock); - - auto statsIt = mConfigStats.find(key); - if (statsIt == mConfigStats.end()) { - return; - } - statsIt->second->matcher_stats[id]++; -} - -void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) { - lock_guard<std::mutex> lock(mLock); - auto statsIt = mConfigStats.find(key); - if (statsIt == mConfigStats.end()) { - return; - } - statsIt->second->alert_stats[id]++; -} - -void StatsdStats::noteRegisteredAnomalyAlarmChanged() { - lock_guard<std::mutex> lock(mLock); - mAnomalyAlarmRegisteredStats++; -} - -void StatsdStats::noteRegisteredPeriodicAlarmChanged() { - lock_guard<std::mutex> lock(mLock); - mPeriodicAlarmRegisteredStats++; -} - -void StatsdStats::updateMinPullIntervalSec(int pullAtomId, long intervalSec) { - lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[pullAtomId].minPullIntervalSec = - std::min(mPulledAtomStats[pullAtomId].minPullIntervalSec, intervalSec); -} - -void StatsdStats::notePull(int pullAtomId) { - lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[pullAtomId].totalPull++; -} - -void StatsdStats::notePullFromCache(int pullAtomId) { - lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[pullAtomId].totalPullFromCache++; -} - -void StatsdStats::notePullTime(int pullAtomId, int64_t pullTimeNs) { - lock_guard<std::mutex> lock(mLock); - auto& pullStats = mPulledAtomStats[pullAtomId]; - pullStats.maxPullTimeNs = std::max(pullStats.maxPullTimeNs, pullTimeNs); - pullStats.avgPullTimeNs = (pullStats.avgPullTimeNs * pullStats.numPullTime + pullTimeNs) / - (pullStats.numPullTime + 1); - pullStats.numPullTime += 1; -} - -void StatsdStats::notePullDelay(int pullAtomId, int64_t pullDelayNs) { - lock_guard<std::mutex> lock(mLock); - auto& pullStats = mPulledAtomStats[pullAtomId]; - pullStats.maxPullDelayNs = std::max(pullStats.maxPullDelayNs, pullDelayNs); - pullStats.avgPullDelayNs = - (pullStats.avgPullDelayNs * pullStats.numPullDelay + pullDelayNs) / - (pullStats.numPullDelay + 1); - pullStats.numPullDelay += 1; -} - -void StatsdStats::notePullDataError(int pullAtomId) { - lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[pullAtomId].dataError++; -} - -void StatsdStats::notePullTimeout(int pullAtomId, - int64_t pullUptimeMillis, - int64_t pullElapsedMillis) { - lock_guard<std::mutex> lock(mLock); - PulledAtomStats& pulledAtomStats = mPulledAtomStats[pullAtomId]; - pulledAtomStats.pullTimeout++; - - if (pulledAtomStats.pullTimeoutMetadata.size() == kMaxTimestampCount) { - pulledAtomStats.pullTimeoutMetadata.pop_front(); - } - - pulledAtomStats.pullTimeoutMetadata.emplace_back(pullUptimeMillis, pullElapsedMillis); -} - -void StatsdStats::notePullExceedMaxDelay(int pullAtomId) { - lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[pullAtomId].pullExceedMaxDelay++; -} - -void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) { - lock_guard<std::mutex> lock(mLock); - - if (atomId <= kMaxPushedAtomId) { - mPushedAtomStats[atomId]++; - } else { - if (mNonPlatformPushedAtomStats.size() < kMaxNonPlatformPushedAtoms) { - mNonPlatformPushedAtomStats[atomId]++; - } - } -} - -void StatsdStats::noteSystemServerRestart(int32_t timeSec) { - lock_guard<std::mutex> lock(mLock); - - if (mSystemServerRestartSec.size() == kMaxSystemServerRestarts) { - mSystemServerRestartSec.pop_front(); - } - mSystemServerRestartSec.push_back(timeSec); -} - -void StatsdStats::notePullFailed(int atomId) { - lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[atomId].pullFailed++; -} - -void StatsdStats::notePullUidProviderNotFound(int atomId) { - lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[atomId].pullUidProviderNotFound++; -} - -void StatsdStats::notePullerNotFound(int atomId) { - lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[atomId].pullerNotFound++; -} - -void StatsdStats::notePullBinderCallFailed(int atomId) { - lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[atomId].binderCallFailCount++; -} - -void StatsdStats::noteEmptyData(int atomId) { - lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[atomId].emptyData++; -} - -void StatsdStats::notePullerCallbackRegistrationChanged(int atomId, bool registered) { - lock_guard<std::mutex> lock(mLock); - if (registered) { - mPulledAtomStats[atomId].registeredCount++; - } else { - mPulledAtomStats[atomId].unregisteredCount++; - } -} - -void StatsdStats::noteHardDimensionLimitReached(int64_t metricId) { - lock_guard<std::mutex> lock(mLock); - getAtomMetricStats(metricId).hardDimensionLimitReached++; -} - -void StatsdStats::noteLateLogEventSkipped(int64_t metricId) { - lock_guard<std::mutex> lock(mLock); - getAtomMetricStats(metricId).lateLogEventSkipped++; -} - -void StatsdStats::noteSkippedForwardBuckets(int64_t metricId) { - lock_guard<std::mutex> lock(mLock); - getAtomMetricStats(metricId).skippedForwardBuckets++; -} - -void StatsdStats::noteBadValueType(int64_t metricId) { - lock_guard<std::mutex> lock(mLock); - getAtomMetricStats(metricId).badValueType++; -} - -void StatsdStats::noteBucketDropped(int64_t metricId) { - lock_guard<std::mutex> lock(mLock); - getAtomMetricStats(metricId).bucketDropped++; -} - -void StatsdStats::noteBucketUnknownCondition(int64_t metricId) { - lock_guard<std::mutex> lock(mLock); - getAtomMetricStats(metricId).bucketUnknownCondition++; -} - -void StatsdStats::noteConditionChangeInNextBucket(int64_t metricId) { - lock_guard<std::mutex> lock(mLock); - getAtomMetricStats(metricId).conditionChangeInNextBucket++; -} - -void StatsdStats::noteInvalidatedBucket(int64_t metricId) { - lock_guard<std::mutex> lock(mLock); - getAtomMetricStats(metricId).invalidatedBucket++; -} - -void StatsdStats::noteBucketCount(int64_t metricId) { - lock_guard<std::mutex> lock(mLock); - getAtomMetricStats(metricId).bucketCount++; -} - -void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs) { - lock_guard<std::mutex> lock(mLock); - AtomMetricStats& pullStats = getAtomMetricStats(metricId); - pullStats.maxBucketBoundaryDelayNs = - std::max(pullStats.maxBucketBoundaryDelayNs, timeDelayNs); - pullStats.minBucketBoundaryDelayNs = - std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs); -} - -void StatsdStats::noteAtomError(int atomTag, bool pull) { - lock_guard<std::mutex> lock(mLock); - if (pull) { - mPulledAtomStats[atomTag].atomErrorCount++; - return; - } - - bool present = (mPushedAtomErrorStats.find(atomTag) != mPushedAtomErrorStats.end()); - bool full = (mPushedAtomErrorStats.size() >= (size_t)kMaxPushedAtomErrorStatsSize); - if (!full || present) { - mPushedAtomErrorStats[atomTag]++; - } -} - -StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) { - auto atomMetricStatsIter = mAtomMetricStats.find(metricId); - if (atomMetricStatsIter != mAtomMetricStats.end()) { - return atomMetricStatsIter->second; - } - auto emplaceResult = mAtomMetricStats.emplace(metricId, AtomMetricStats()); - return emplaceResult.first->second; -} - -void StatsdStats::reset() { - lock_guard<std::mutex> lock(mLock); - resetInternalLocked(); -} - -void StatsdStats::resetInternalLocked() { - // Reset the historical data, but keep the active ConfigStats - mStartTimeSec = getWallClockSec(); - mIceBox.clear(); - std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0); - mNonPlatformPushedAtomStats.clear(); - mAnomalyAlarmRegisteredStats = 0; - mPeriodicAlarmRegisteredStats = 0; - mSystemServerRestartSec.clear(); - mLogLossStats.clear(); - mOverflowCount = 0; - mMinQueueHistoryNs = kInt64Max; - mMaxQueueHistoryNs = 0; - for (auto& config : mConfigStats) { - config.second->broadcast_sent_time_sec.clear(); - config.second->activation_time_sec.clear(); - config.second->deactivation_time_sec.clear(); - config.second->data_drop_time_sec.clear(); - config.second->data_drop_bytes.clear(); - config.second->dump_report_stats.clear(); - config.second->annotations.clear(); - config.second->matcher_stats.clear(); - config.second->condition_stats.clear(); - config.second->metric_stats.clear(); - config.second->metric_dimension_in_condition_stats.clear(); - config.second->alert_stats.clear(); - } - for (auto& pullStats : mPulledAtomStats) { - pullStats.second.totalPull = 0; - pullStats.second.totalPullFromCache = 0; - pullStats.second.minPullIntervalSec = LONG_MAX; - pullStats.second.avgPullTimeNs = 0; - pullStats.second.maxPullTimeNs = 0; - pullStats.second.numPullTime = 0; - pullStats.second.avgPullDelayNs = 0; - pullStats.second.maxPullDelayNs = 0; - pullStats.second.numPullDelay = 0; - pullStats.second.dataError = 0; - pullStats.second.pullTimeout = 0; - pullStats.second.pullExceedMaxDelay = 0; - pullStats.second.pullFailed = 0; - pullStats.second.pullUidProviderNotFound = 0; - pullStats.second.pullerNotFound = 0; - pullStats.second.registeredCount = 0; - pullStats.second.unregisteredCount = 0; - pullStats.second.atomErrorCount = 0; - pullStats.second.binderCallFailCount = 0; - pullStats.second.pullTimeoutMetadata.clear(); - } - mAtomMetricStats.clear(); - mActivationBroadcastGuardrailStats.clear(); - mPushedAtomErrorStats.clear(); -} - -string buildTimeString(int64_t timeSec) { - time_t t = timeSec; - struct tm* tm = localtime(&t); - char timeBuffer[80]; - strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p", tm); - return string(timeBuffer); -} - -int StatsdStats::getPushedAtomErrors(int atomId) const { - const auto& it = mPushedAtomErrorStats.find(atomId); - if (it != mPushedAtomErrorStats.end()) { - return it->second; - } else { - return 0; - } -} - -void StatsdStats::dumpStats(int out) const { - lock_guard<std::mutex> lock(mLock); - time_t t = mStartTimeSec; - struct tm* tm = localtime(&t); - char timeBuffer[80]; - strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm); - dprintf(out, "Stats collection start second: %s\n", timeBuffer); - dprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size()); - for (const auto& configStats : mIceBox) { - dprintf(out, - "Config {%d_%lld}: creation=%d, deletion=%d, reset=%d, #metric=%d, #condition=%d, " - "#matcher=%d, #alert=%d, valid=%d\n", - configStats->uid, (long long)configStats->id, configStats->creation_time_sec, - configStats->deletion_time_sec, configStats->reset_time_sec, - configStats->metric_count, configStats->condition_count, configStats->matcher_count, - configStats->alert_count, configStats->is_valid); - - for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { - dprintf(out, "\tbroadcast time: %d\n", broadcastTime); - } - - for (const int& activationTime : configStats->activation_time_sec) { - dprintf(out, "\tactivation time: %d\n", activationTime); - } - - for (const int& deactivationTime : configStats->deactivation_time_sec) { - dprintf(out, "\tdeactivation time: %d\n", deactivationTime); - } - - auto dropTimePtr = configStats->data_drop_time_sec.begin(); - auto dropBytesPtr = configStats->data_drop_bytes.begin(); - for (int i = 0; i < (int)configStats->data_drop_time_sec.size(); - i++, dropTimePtr++, dropBytesPtr++) { - dprintf(out, "\tdata drop time: %d with size %lld", *dropTimePtr, - (long long)*dropBytesPtr); - } - } - dprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size()); - for (auto& pair : mConfigStats) { - auto& configStats = pair.second; - dprintf(out, - "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " - "#matcher=%d, #alert=%d, valid=%d\n", - configStats->uid, (long long)configStats->id, configStats->creation_time_sec, - configStats->deletion_time_sec, configStats->metric_count, - configStats->condition_count, configStats->matcher_count, configStats->alert_count, - configStats->is_valid); - for (const auto& annotation : configStats->annotations) { - dprintf(out, "\tannotation: %lld, %d\n", (long long)annotation.first, - annotation.second); - } - - for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { - dprintf(out, "\tbroadcast time: %s(%lld)\n", buildTimeString(broadcastTime).c_str(), - (long long)broadcastTime); - } - - for (const int& activationTime : configStats->activation_time_sec) { - dprintf(out, "\tactivation time: %d\n", activationTime); - } - - for (const int& deactivationTime : configStats->deactivation_time_sec) { - dprintf(out, "\tdeactivation time: %d\n", deactivationTime); - } - - auto dropTimePtr = configStats->data_drop_time_sec.begin(); - auto dropBytesPtr = configStats->data_drop_bytes.begin(); - for (int i = 0; i < (int)configStats->data_drop_time_sec.size(); - i++, dropTimePtr++, dropBytesPtr++) { - dprintf(out, "\tdata drop time: %s(%lld) with %lld bytes\n", - buildTimeString(*dropTimePtr).c_str(), (long long)*dropTimePtr, - (long long)*dropBytesPtr); - } - - for (const auto& dump : configStats->dump_report_stats) { - dprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n", - buildTimeString(dump.first).c_str(), (long long)dump.first, - (long long)dump.second); - } - - for (const auto& stats : pair.second->matcher_stats) { - dprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second); - } - - for (const auto& stats : pair.second->condition_stats) { - dprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first, - stats.second); - } - - for (const auto& stats : pair.second->condition_stats) { - dprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first, - stats.second); - } - - for (const auto& stats : pair.second->alert_stats) { - dprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second); - } - } - dprintf(out, "********Disk Usage stats***********\n"); - StorageManager::printStats(out); - dprintf(out, "********Pushed Atom stats***********\n"); - const size_t atomCounts = mPushedAtomStats.size(); - for (size_t i = 2; i < atomCounts; i++) { - if (mPushedAtomStats[i] > 0) { - dprintf(out, "Atom %zu->(total count)%d, (error count)%d\n", i, mPushedAtomStats[i], - getPushedAtomErrors((int)i)); - } - } - for (const auto& pair : mNonPlatformPushedAtomStats) { - dprintf(out, "Atom %d->(total count)%d, (error count)%d\n", pair.first, pair.second, - getPushedAtomErrors(pair.first)); - } - - dprintf(out, "********Pulled Atom stats***********\n"); - for (const auto& pair : mPulledAtomStats) { - dprintf(out, - "Atom %d->(total pull)%ld, (pull from cache)%ld, " - "(pull failed)%ld, (min pull interval)%ld \n" - " (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay " - "nanos)%lld, " - " (max pull delay nanos)%lld, (data error)%ld\n" - " (pull timeout)%ld, (pull exceed max delay)%ld" - " (no uid provider count)%ld, (no puller found count)%ld\n" - " (registered count) %ld, (unregistered count) %ld" - " (atom error count) %d\n", - (int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache, - (long)pair.second.pullFailed, (long)pair.second.minPullIntervalSec, - (long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs, - (long long)pair.second.avgPullDelayNs, (long long)pair.second.maxPullDelayNs, - pair.second.dataError, pair.second.pullTimeout, pair.second.pullExceedMaxDelay, - pair.second.pullUidProviderNotFound, pair.second.pullerNotFound, - pair.second.registeredCount, pair.second.unregisteredCount, - pair.second.atomErrorCount); - if (pair.second.pullTimeoutMetadata.size() > 0) { - string uptimeMillis = "(pull timeout system uptime millis) "; - string pullTimeoutMillis = "(pull timeout elapsed time millis) "; - for (const auto& stats : pair.second.pullTimeoutMetadata) { - uptimeMillis.append(to_string(stats.pullTimeoutUptimeMillis)).append(",");; - pullTimeoutMillis.append(to_string(stats.pullTimeoutElapsedMillis)).append(","); - } - uptimeMillis.pop_back(); - uptimeMillis.push_back('\n'); - pullTimeoutMillis.pop_back(); - pullTimeoutMillis.push_back('\n'); - dprintf(out, "%s", uptimeMillis.c_str()); - dprintf(out, "%s", pullTimeoutMillis.c_str()); - } - } - - if (mAnomalyAlarmRegisteredStats > 0) { - dprintf(out, "********AnomalyAlarmStats stats***********\n"); - dprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats); - } - - if (mPeriodicAlarmRegisteredStats > 0) { - dprintf(out, "********SubscriberAlarmStats stats***********\n"); - dprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats); - } - - dprintf(out, "UID map stats: bytes=%d, changes=%d, deleted=%d, changes lost=%d\n", - mUidMapStats.bytes_used, mUidMapStats.changes, mUidMapStats.deleted_apps, - mUidMapStats.dropped_changes); - - for (const auto& restart : mSystemServerRestartSec) { - dprintf(out, "System server restarts at %s(%lld)\n", buildTimeString(restart).c_str(), - (long long)restart); - } - - for (const auto& loss : mLogLossStats) { - dprintf(out, - "Log loss: %lld (wall clock sec) - %d (count), %d (last error), %d (last tag), %d " - "(uid), %d (pid)\n", - (long long)loss.mWallClockSec, loss.mCount, loss.mLastError, loss.mLastTag, - loss.mUid, loss.mPid); - } - - dprintf(out, "Event queue overflow: %d; MaxHistoryNs: %lld; MinHistoryNs: %lld\n", - mOverflowCount, (long long)mMaxQueueHistoryNs, (long long)mMinQueueHistoryNs); - - if (mActivationBroadcastGuardrailStats.size() > 0) { - dprintf(out, "********mActivationBroadcastGuardrail stats***********\n"); - for (const auto& pair: mActivationBroadcastGuardrailStats) { - dprintf(out, "Uid %d: Times: ", pair.first); - for (const auto& guardrailHitTime : pair.second) { - dprintf(out, "%d ", guardrailHitTime); - } - } - dprintf(out, "\n"); - } -} - -void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* proto) { - uint64_t token = - proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_UID, configStats.uid); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_ID, (long long)configStats.id); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CREATION, configStats.creation_time_sec); - if (configStats.reset_time_sec != 0) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_RESET, configStats.reset_time_sec); - } - if (configStats.deletion_time_sec != 0) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DELETION, - configStats.deletion_time_sec); - } - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_METRIC_COUNT, configStats.metric_count); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CONDITION_COUNT, - configStats.condition_count); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_MATCHER_COUNT, configStats.matcher_count); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ALERT_COUNT, configStats.alert_count); - proto->write(FIELD_TYPE_BOOL | FIELD_ID_CONFIG_STATS_VALID, configStats.is_valid); - - for (const auto& broadcast : configStats.broadcast_sent_time_sec) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_BROADCAST | FIELD_COUNT_REPEATED, - broadcast); - } - - for (const auto& activation : configStats.activation_time_sec) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ACTIVATION | FIELD_COUNT_REPEATED, - activation); - } - - for (const auto& deactivation : configStats.deactivation_time_sec) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DEACTIVATION | FIELD_COUNT_REPEATED, - deactivation); - } - - for (const auto& drop_time : configStats.data_drop_time_sec) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP_TIME | FIELD_COUNT_REPEATED, - drop_time); - } - - for (const auto& drop_bytes : configStats.data_drop_bytes) { - proto->write( - FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_DATA_DROP_BYTES | FIELD_COUNT_REPEATED, - (long long)drop_bytes); - } - - for (const auto& dump : configStats.dump_report_stats) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME | - FIELD_COUNT_REPEATED, - dump.first); - } - - for (const auto& dump : configStats.dump_report_stats) { - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES | - FIELD_COUNT_REPEATED, - (long long)dump.second); - } - - for (const auto& annotation : configStats.annotations) { - uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_ANNOTATION); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_ANNOTATION_INT64, - (long long)annotation.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ANNOTATION_INT32, annotation.second); - proto->end(token); - } - - for (const auto& pair : configStats.matcher_stats) { - uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_MATCHER_STATS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_MATCHER_STATS_ID, (long long)pair.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_MATCHER_STATS_COUNT, pair.second); - proto->end(tmpToken); - } - - for (const auto& pair : configStats.condition_stats) { - uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_CONDITION_STATS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_STATS_ID, (long long)pair.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONDITION_STATS_COUNT, pair.second); - proto->end(tmpToken); - } - - for (const auto& pair : configStats.metric_stats) { - uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_METRIC_STATS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second); - proto->end(tmpToken); - } - for (const auto& pair : configStats.metric_dimension_in_condition_stats) { - uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second); - proto->end(tmpToken); - } - - for (const auto& pair : configStats.alert_stats) { - uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_ALERT_STATS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_STATS_ID, (long long)pair.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_ALERT_STATS_COUNT, pair.second); - proto->end(tmpToken); - } - - proto->end(token); -} - -void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { - lock_guard<std::mutex> lock(mLock); - - ProtoOutputStream proto; - proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTimeSec); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)getWallClockSec()); - - for (const auto& configStats : mIceBox) { - addConfigStatsToProto(*configStats, &proto); - } - - for (auto& pair : mConfigStats) { - addConfigStatsToProto(*(pair.second), &proto); - } - - const size_t atomCounts = mPushedAtomStats.size(); - for (size_t i = 2; i < atomCounts; i++) { - if (mPushedAtomStats[i] > 0) { - uint64_t token = - proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]); - int errors = getPushedAtomErrors(i); - if (errors > 0) { - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors); - } - proto.end(token); - } - } - - for (const auto& pair : mNonPlatformPushedAtomStats) { - uint64_t token = - proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, pair.first); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, pair.second); - int errors = getPushedAtomErrors(pair.first); - if (errors > 0) { - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors); - } - proto.end(token); - } - - for (const auto& pair : mPulledAtomStats) { - android::os::statsd::writePullerStatsToStream(pair, &proto); - } - - for (const auto& pair : mAtomMetricStats) { - android::os::statsd::writeAtomMetricStatsToStream(pair, &proto); - } - - if (mAnomalyAlarmRegisteredStats > 0) { - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ANOMALY_ALARMS_REGISTERED, - mAnomalyAlarmRegisteredStats); - proto.end(token); - } - - if (mPeriodicAlarmRegisteredStats > 0) { - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PERIODIC_ALARM_STATS); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_PERIODIC_ALARMS_REGISTERED, - mPeriodicAlarmRegisteredStats); - proto.end(token); - } - - uint64_t uidMapToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_CHANGES, mUidMapStats.changes); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_BYTES_USED, mUidMapStats.bytes_used); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_CHANGES, mUidMapStats.dropped_changes); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DELETED_APPS, mUidMapStats.deleted_apps); - proto.end(uidMapToken); - - for (const auto& error : mLogLossStats) { - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_LOGGER_ERROR_STATS | - FIELD_COUNT_REPEATED); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_TIME, error.mWallClockSec); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_COUNT, error.mCount); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_ERROR, error.mLastError); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_TAG, error.mLastTag); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_UID, error.mUid); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_PID, error.mPid); - proto.end(token); - } - - if (mOverflowCount > 0) { - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_OVERFLOW); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_OVERFLOW_COUNT, (int32_t)mOverflowCount); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_OVERFLOW_MAX_HISTORY, - (long long)mMaxQueueHistoryNs); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_OVERFLOW_MIN_HISTORY, - (long long)mMinQueueHistoryNs); - proto.end(token); - } - - for (const auto& restart : mSystemServerRestartSec) { - proto.write(FIELD_TYPE_INT32 | FIELD_ID_SYSTEM_SERVER_RESTART | FIELD_COUNT_REPEATED, - restart); - } - - for (const auto& pair: mActivationBroadcastGuardrailStats) { - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | - FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL | - FIELD_COUNT_REPEATED); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID, - (int32_t) pair.first); - for (const auto& guardrailHitTime : pair.second) { - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME | - FIELD_COUNT_REPEATED, - guardrailHitTime); - } - proto.end(token); - } - - output->clear(); - size_t bufferSize = proto.size(); - output->resize(bufferSize); - - size_t pos = 0; - sp<android::util::ProtoReader> reader = proto.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((*output)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - if (reset) { - resetInternalLocked(); - } - - VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)bufferSize); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h deleted file mode 100644 index 005048446fc3..000000000000 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ /dev/null @@ -1,678 +0,0 @@ -/* - * 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 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. - */ -#pragma once - -#include "config/ConfigKey.h" - -#include <gtest/gtest_prod.h> -#include <log/log_time.h> -#include <list> -#include <mutex> -#include <string> -#include <vector> -#include <unordered_map> - -namespace android { -namespace os { -namespace statsd { - -struct ConfigStats { - int32_t uid; - int64_t id; - int32_t creation_time_sec; - int32_t deletion_time_sec = 0; - int32_t reset_time_sec = 0; - int32_t metric_count; - int32_t condition_count; - int32_t matcher_count; - int32_t alert_count; - bool is_valid; - - std::list<int32_t> broadcast_sent_time_sec; - - // Times at which this config is activated. - std::list<int32_t> activation_time_sec; - - // Times at which this config is deactivated. - std::list<int32_t> deactivation_time_sec; - - std::list<int32_t> data_drop_time_sec; - // Number of bytes dropped at corresponding time. - std::list<int64_t> data_drop_bytes; - std::list<std::pair<int32_t, int64_t>> dump_report_stats; - - // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount. - std::map<const int64_t, int> matcher_stats; - - // Stores the number of output tuple of condition trackers when it's bigger than - // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, - // it means some data has been dropped. The map size is capped by kMaxConfigCount. - std::map<const int64_t, int> condition_stats; - - // Stores the number of output tuple of metric producers when it's bigger than - // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, - // it means some data has been dropped. The map size is capped by kMaxConfigCount. - std::map<const int64_t, int> metric_stats; - - // Stores the max number of output tuple of dimensions in condition across dimensions in what - // when it's bigger than kDimensionKeySizeSoftLimit. When you see the number is - // kDimensionKeySizeHardLimit +1, it means some data has been dropped. The map size is capped by - // kMaxConfigCount. - std::map<const int64_t, int> metric_dimension_in_condition_stats; - - // Stores the number of times an anomaly detection alert has been declared. - // The map size is capped by kMaxConfigCount. - std::map<const int64_t, int> alert_stats; - - // Stores the config ID for each sub-config used. - std::list<std::pair<const int64_t, const int32_t>> annotations; -}; - -struct UidMapStats { - int32_t changes; - int32_t bytes_used; - int32_t dropped_changes; - int32_t deleted_apps = 0; -}; - -// Keeps track of stats of statsd. -// Single instance shared across the process. All public methods are thread safe. -class StatsdStats { -public: - static StatsdStats& getInstance(); - ~StatsdStats(){}; - - const static int kDimensionKeySizeSoftLimit = 500; - const static int kDimensionKeySizeHardLimit = 800; - - // Per atom dimension key size limit - static const std::map<int, std::pair<size_t, size_t>> kAtomDimensionKeySizeLimitMap; - - const static int kMaxConfigCountPerUid = 20; - const static int kMaxAlertCountPerConfig = 100; - const static int kMaxConditionCountPerConfig = 300; - const static int kMaxMetricCountPerConfig = 1000; - const static int kMaxMatcherCountPerConfig = 800; - - // The max number of old config stats we keep. - const static int kMaxIceBoxSize = 20; - - const static int kMaxLoggerErrors = 20; - - const static int kMaxSystemServerRestarts = 20; - - const static int kMaxTimestampCount = 20; - - const static int kMaxLogSourceCount = 50; - - const static int kMaxPullAtomPackages = 100; - - // Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd - // drops the metrics data in memory. - static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024; - - // Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the - // data subscriber that it's time to call getData. - static const size_t kBytesPerConfigTriggerGetData = 192 * 1024; - - // Cap the UID map's memory usage to this. This should be fairly high since the UID information - // is critical for understanding the metrics. - const static size_t kMaxBytesUsedUidMap = 50 * 1024; - - // The number of deleted apps that are stored in the uid map. - const static int kMaxDeletedAppsInUidMap = 100; - - /* Minimum period between two broadcasts in nanoseconds. */ - static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC; - - /* Min period between two checks of byte size per config key in nanoseconds. */ - static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC; - - /* Minimum period between two activation broadcasts in nanoseconds. */ - static const int64_t kMinActivationBroadcastPeriodNs = 10 * NS_PER_SEC; - - // Maximum age (30 days) that files on disk can exist in seconds. - static const int kMaxAgeSecond = 60 * 60 * 24 * 30; - - // Maximum age (2 days) that local history files on disk can exist in seconds. - static const int kMaxLocalHistoryAgeSecond = 60 * 60 * 24 * 2; - - // Maximum number of files (1000) that can be in stats directory on disk. - static const int kMaxFileNumber = 1000; - - // Maximum size of all files that can be written to stats directory on disk. - static const int kMaxFileSize = 50 * 1024 * 1024; - - // How long to try to clear puller cache from last time - static const long kPullerCacheClearIntervalSec = 1; - - // Max time to do a pull. - static const int64_t kPullMaxDelayNs = 30 * NS_PER_SEC; - - // Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId. - static const int kMaxNonPlatformPushedAtoms = 100; - - // Maximum atom id value that we consider a platform pushed atom. - // This should be updated once highest pushed atom id in atoms.proto approaches this value. - static const int kMaxPushedAtomId = 500; - - // Atom id that is the start of the pulled atoms. - static const int kPullAtomStartTag = 10000; - - // Atom id that is the start of vendor atoms. - static const int kVendorAtomStartTag = 100000; - - // Vendor pulled atom start id. - static const int32_t kVendorPulledAtomStartTag = 150000; - - // Beginning of range for timestamp truncation. - static const int32_t kTimestampTruncationStartTag = 300000; - - // End of range for timestamp truncation. - static const int32_t kTimestampTruncationEndTag = 304999; - - // Max accepted atom id. - static const int32_t kMaxAtomTag = 200000; - - static const int64_t kInt64Max = 0x7fffffffffffffffLL; - - static const int32_t kMaxLoggedBucketDropEvents = 10; - - /** - * Report a new config has been received and report the static stats about the config. - * - * The static stats include: the count of metrics, conditions, matchers, and alerts. - * If the config is not valid, this config stats will be put into icebox immediately. - */ - void noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount, - int matchersCount, int alertCount, - const std::list<std::pair<const int64_t, const int32_t>>& annotations, - bool isValid); - /** - * Report a config has been removed. - */ - void noteConfigRemoved(const ConfigKey& key); - /** - * Report a config has been reset when ttl expires. - */ - void noteConfigReset(const ConfigKey& key); - - /** - * Report a broadcast has been sent to a config owner to collect the data. - */ - void noteBroadcastSent(const ConfigKey& key); - - /** - * Report that a config has become activated or deactivated. - * This can be different from whether or not a broadcast is sent if the - * guardrail prevented the broadcast from being sent. - */ - void noteActiveStatusChanged(const ConfigKey& key, bool activate); - - /** - * Report a config's metrics data has been dropped. - */ - void noteDataDropped(const ConfigKey& key, const size_t totalBytes); - - /** - * Report metrics data report has been sent. - * - * The report may be requested via StatsManager API, or through adb cmd. - */ - void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes); - - /** - * Report the size of output tuple of a condition. - * - * Note: only report when the condition has an output dimension, and the tuple - * count > kDimensionKeySizeSoftLimit. - * - * [key]: The config key that this condition belongs to. - * [id]: The id of the condition. - * [size]: The output tuple size. - */ - void noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size); - - /** - * Report the size of output tuple of a metric. - * - * Note: only report when the metric has an output dimension, and the tuple - * count > kDimensionKeySizeSoftLimit. - * - * [key]: The config key that this metric belongs to. - * [id]: The id of the metric. - * [size]: The output tuple size. - */ - void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size); - - /** - * Report the max size of output tuple of dimension in condition across dimensions in what. - * - * Note: only report when the metric has an output dimension in condition, and the max tuple - * count > kDimensionKeySizeSoftLimit. - * - * [key]: The config key that this metric belongs to. - * [id]: The id of the metric. - * [size]: The output tuple size. - */ - void noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t& id, int size); - - /** - * Report a matcher has been matched. - * - * [key]: The config key that this matcher belongs to. - * [id]: The id of the matcher. - */ - void noteMatcherMatched(const ConfigKey& key, const int64_t& id); - - /** - * Report that an anomaly detection alert has been declared. - * - * [key]: The config key that this alert belongs to. - * [id]: The id of the alert. - */ - void noteAnomalyDeclared(const ConfigKey& key, const int64_t& id); - - /** - * Report an atom event has been logged. - */ - void noteAtomLogged(int atomId, int32_t timeSec); - - /** - * Report that statsd modified the anomaly alarm registered with StatsCompanionService. - */ - void noteRegisteredAnomalyAlarmChanged(); - - /** - * Report that statsd modified the periodic alarm registered with StatsCompanionService. - */ - void noteRegisteredPeriodicAlarmChanged(); - - /** - * Records the number of delta entries that are being dropped from the uid map. - */ - void noteUidMapDropped(int deltas); - - /** - * Records that an app was deleted (from statsd's map). - */ - void noteUidMapAppDeletionDropped(); - - /** - * Updates the number of changes currently stored in the uid map. - */ - void setUidMapChanges(int changes); - void setCurrentUidMapMemory(int bytes); - - /* - * Updates minimum interval between pulls for an pulled atom. - */ - void updateMinPullIntervalSec(int pullAtomId, long intervalSec); - - /* - * Notes an atom is pulled. - */ - void notePull(int pullAtomId); - - /* - * Notes an atom is served from puller cache. - */ - void notePullFromCache(int pullAtomId); - - /* - * Notify data error for pulled atom. - */ - void notePullDataError(int pullAtomId); - - /* - * Records time for actual pulling, not including those served from cache and not including - * statsd processing delays. - */ - void notePullTime(int pullAtomId, int64_t pullTimeNs); - - /* - * Records pull delay for a pulled atom, including those served from cache and including statsd - * processing delays. - */ - void notePullDelay(int pullAtomId, int64_t pullDelayNs); - - /* - * Records pull exceeds timeout for the puller. - */ - void notePullTimeout(int pullAtomId, int64_t pullUptimeMillis, int64_t pullElapsedMillis); - - /* - * Records pull exceeds max delay for a metric. - */ - void notePullExceedMaxDelay(int pullAtomId); - - /* - * Records when system server restarts. - */ - void noteSystemServerRestart(int32_t timeSec); - - /** - * Records statsd skipped an event. - */ - void noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError, - int32_t lastAtomTag, int32_t uid, int32_t pid); - - /** - * Records that the pull of an atom has failed. Eg, if the client indicated the pull failed, if - * the pull timed out, or if the outgoing binder call failed. - * This count will only increment if the puller was actually invoked. - * - * It does not include a pull not occurring due to not finding the appropriate - * puller. These cases are covered in other counts. - */ - void notePullFailed(int atomId); - - /** - * Records that the pull of an atom has failed due to not having a uid provider. - */ - void notePullUidProviderNotFound(int atomId); - - /** - * Records that the pull of an atom has failed due not finding a puller registered by a - * trusted uid. - */ - void notePullerNotFound(int atomId); - - /** - * Records that the pull has failed due to the outgoing binder call failing. - */ - void notePullBinderCallFailed(int atomId); - - /** - * A pull with no data occurred - */ - void noteEmptyData(int atomId); - - /** - * Records that a puller callback for the given atomId was registered or unregistered. - * - * @param registered True if the callback was registered, false if was unregistered. - */ - void notePullerCallbackRegistrationChanged(int atomId, bool registered); - - /** - * Hard limit was reached in the cardinality of an atom - */ - void noteHardDimensionLimitReached(int64_t metricId); - - /** - * A log event was too late, arrived in the wrong bucket and was skipped - */ - void noteLateLogEventSkipped(int64_t metricId); - - /** - * Buckets were skipped as time elapsed without any data for them - */ - void noteSkippedForwardBuckets(int64_t metricId); - - /** - * An unsupported value type was received - */ - void noteBadValueType(int64_t metricId); - - /** - * Buckets were dropped due to reclaim memory. - */ - void noteBucketDropped(int64_t metricId); - - /** - * A condition change was too late, arrived in the wrong bucket and was skipped - */ - void noteConditionChangeInNextBucket(int64_t metricId); - - /** - * A bucket has been tagged as invalid. - */ - void noteInvalidatedBucket(int64_t metricId); - - /** - * Tracks the total number of buckets (include skipped/invalid buckets). - */ - void noteBucketCount(int64_t metricId); - - /** - * For pulls at bucket boundaries, it represents the misalignment between the real timestamp and - * the end of the bucket. - */ - void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs); - - /** - * Number of buckets with unknown condition. - */ - void noteBucketUnknownCondition(int64_t metricId); - - /* Reports one event has been dropped due to queue overflow, and the oldest event timestamp in - * the queue */ - void noteEventQueueOverflow(int64_t oldestEventTimestampNs); - - /** - * Reports that the activation broadcast guardrail was hit for this uid. Namely, the broadcast - * should have been sent, but instead was skipped due to hitting the guardrail. - */ - void noteActivationBroadcastGuardrailHit(const int uid); - - /** - * Reports that an atom is erroneous or cannot be parsed successfully by - * statsd. An atom tag of 0 indicates that the client did not supply the - * atom id within the encoding. - * - * For pushed atoms only, this call should be preceded by a call to - * noteAtomLogged. - */ - void noteAtomError(int atomTag, bool pull=false); - - /** - * Reset the historical stats. Including all stats in icebox, and the tracked stats about - * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue - * to collect stats after reset() has been called. - */ - void reset(); - - /** - * Output the stats in protobuf binary format to [buffer]. - * - * [reset]: whether to clear the historical stats after the call. - */ - void dumpStats(std::vector<uint8_t>* buffer, bool reset); - - /** - * Output statsd stats in human readable format to [out] file descriptor. - */ - void dumpStats(int outFd) const; - - typedef struct PullTimeoutMetadata { - int64_t pullTimeoutUptimeMillis; - int64_t pullTimeoutElapsedMillis; - PullTimeoutMetadata(int64_t uptimeMillis, int64_t elapsedMillis) : - pullTimeoutUptimeMillis(uptimeMillis), - pullTimeoutElapsedMillis(elapsedMillis) {/* do nothing */} - } PullTimeoutMetadata; - - typedef struct { - long totalPull = 0; - long totalPullFromCache = 0; - long minPullIntervalSec = LONG_MAX; - int64_t avgPullTimeNs = 0; - int64_t maxPullTimeNs = 0; - long numPullTime = 0; - int64_t avgPullDelayNs = 0; - int64_t maxPullDelayNs = 0; - long numPullDelay = 0; - long dataError = 0; - long pullTimeout = 0; - long pullExceedMaxDelay = 0; - long pullFailed = 0; - long pullUidProviderNotFound = 0; - long pullerNotFound = 0; - long emptyData = 0; - long registeredCount = 0; - long unregisteredCount = 0; - int32_t atomErrorCount = 0; - long binderCallFailCount = 0; - std::list<PullTimeoutMetadata> pullTimeoutMetadata; - } PulledAtomStats; - - typedef struct { - long hardDimensionLimitReached = 0; - long lateLogEventSkipped = 0; - long skippedForwardBuckets = 0; - long badValueType = 0; - long conditionChangeInNextBucket = 0; - long invalidatedBucket = 0; - long bucketDropped = 0; - int64_t minBucketBoundaryDelayNs = 0; - int64_t maxBucketBoundaryDelayNs = 0; - long bucketUnknownCondition = 0; - long bucketCount = 0; - } AtomMetricStats; - -private: - StatsdStats(); - - mutable std::mutex mLock; - - int32_t mStartTimeSec; - - // Track the number of dropped entries used by the uid map. - UidMapStats mUidMapStats; - - // The stats about the configs that are still in use. - // The map size is capped by kMaxConfigCount. - std::map<const ConfigKey, std::shared_ptr<ConfigStats>> mConfigStats; - - // Stores the stats for the configs that are no longer in use. - // The size of the vector is capped by kMaxIceBoxSize. - std::list<const std::shared_ptr<ConfigStats>> mIceBox; - - // Stores the number of times a pushed atom is logged. - // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms - // out of that range will be put in mNonPlatformPushedAtomStats. - // This is a vector, not a map because it will be accessed A LOT -- for each stats log. - std::vector<int> mPushedAtomStats; - - // Stores the number of times a pushed atom is logged for atom ids above kMaxPushedAtomId. - // The max size of the map is kMaxNonPlatformPushedAtoms. - std::unordered_map<int, int> mNonPlatformPushedAtomStats; - - // Maps PullAtomId to its stats. The size is capped by the puller atom counts. - std::map<int, PulledAtomStats> mPulledAtomStats; - - // Stores the number of times a pushed atom was logged erroneously. The - // corresponding counts for pulled atoms are stored in PulledAtomStats. - // The max size of this map is kMaxAtomErrorsStatsSize. - std::map<int, int> mPushedAtomErrorStats; - int kMaxPushedAtomErrorStatsSize = 100; - - // Maps metric ID to its stats. The size is capped by the number of metrics. - std::map<int64_t, AtomMetricStats> mAtomMetricStats; - - // Maps uids to times when the activation changed broadcast not sent due to hitting the - // guardrail. The size is capped by the number of configs, and up to 20 times per uid. - std::map<int, std::list<int32_t>> mActivationBroadcastGuardrailStats; - - struct LogLossStats { - LogLossStats(int32_t sec, int32_t count, int32_t error, int32_t tag, int32_t uid, - int32_t pid) - : mWallClockSec(sec), - mCount(count), - mLastError(error), - mLastTag(tag), - mUid(uid), - mPid(pid) { - } - int32_t mWallClockSec; - int32_t mCount; - // error code defined in linux/errno.h - int32_t mLastError; - int32_t mLastTag; - int32_t mUid; - int32_t mPid; - }; - - // Max of {(now - oldestEventTimestamp) when overflow happens}. - // This number is helpful to understand how SLOW statsd can be. - int64_t mMaxQueueHistoryNs = 0; - - // Min of {(now - oldestEventTimestamp) when overflow happens}. - // This number is helpful to understand how FAST the events floods to statsd. - int64_t mMinQueueHistoryNs = kInt64Max; - - // Total number of events that are lost due to queue overflow. - int32_t mOverflowCount = 0; - - // Timestamps when we detect log loss, and the number of logs lost. - std::list<LogLossStats> mLogLossStats; - - std::list<int32_t> mSystemServerRestartSec; - - // Stores the number of times statsd modified the anomaly alarm registered with - // StatsCompanionService. - int mAnomalyAlarmRegisteredStats = 0; - - // Stores the number of times statsd registers the periodic alarm changes - int mPeriodicAlarmRegisteredStats = 0; - - void noteConfigResetInternalLocked(const ConfigKey& key); - - void noteConfigRemovedInternalLocked(const ConfigKey& key); - - void resetInternalLocked(); - - void noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec); - - void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes, int32_t timeSec); - - void noteBroadcastSent(const ConfigKey& key, int32_t timeSec); - - void noteActiveStatusChanged(const ConfigKey& key, bool activate, int32_t timeSec); - - void noteActivationBroadcastGuardrailHit(const int uid, int32_t timeSec); - - void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats); - - int getPushedAtomErrors(int atomId) const; - - /** - * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference - * will live as long as `this`. - */ - StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId); - - FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd); - FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd); - FRIEND_TEST(StatsdStatsTest, TestConfigRemove); - FRIEND_TEST(StatsdStatsTest, TestSubStats); - FRIEND_TEST(StatsdStatsTest, TestAtomLog); - FRIEND_TEST(StatsdStatsTest, TestNonPlatformAtomLog); - FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold); - FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor); - FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash); - FRIEND_TEST(StatsdStatsTest, TestPullAtomStats); - FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats); - FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit); - FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats); - - FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/hash.cpp b/cmds/statsd/src/hash.cpp deleted file mode 100644 index 543a748adedb..000000000000 --- a/cmds/statsd/src/hash.cpp +++ /dev/null @@ -1,142 +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. - */ - -#include "hash.h" - -#ifndef FALLTHROUGH_INTENDED -#define FALLTHROUGH_INTENDED [[fallthrough]] -#endif - -namespace android { -namespace os { -namespace statsd { - -namespace { -// Lower-level versions of Get... that read directly from a character buffer -// without any bounds checking. -inline uint32_t DecodeFixed32(const char *ptr) { - return ((static_cast<uint32_t>(static_cast<unsigned char>(ptr[0]))) | - (static_cast<uint32_t>(static_cast<unsigned char>(ptr[1])) << 8) | - (static_cast<uint32_t>(static_cast<unsigned char>(ptr[2])) << 16) | - (static_cast<uint32_t>(static_cast<unsigned char>(ptr[3])) << 24)); -} - -inline uint64_t DecodeFixed64(const char* ptr) { - uint64_t lo = DecodeFixed32(ptr); - uint64_t hi = DecodeFixed32(ptr + 4); - return (hi << 32) | lo; -} - -// 0xff is in case char is signed. -static inline uint32_t ByteAs32(char c) { return static_cast<uint32_t>(c) & 0xff; } -static inline uint64_t ByteAs64(char c) { return static_cast<uint64_t>(c) & 0xff; } - -} // namespace - -uint32_t Hash32(const char *data, size_t n, uint32_t seed) { - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - const uint32_t m = 0x5bd1e995; - const int r = 24; - - // Initialize the hash to a 'random' value - uint32_t h = static_cast<uint32_t>(seed ^ n); - - // Mix 4 bytes at a time into the hash - while (n >= 4) { - uint32_t k = DecodeFixed32(data); - k *= m; - k ^= k >> r; - k *= m; - h *= m; - h ^= k; - data += 4; - n -= 4; - } - - // Handle the last few bytes of the input array - switch (n) { - case 3: - h ^= ByteAs32(data[2]) << 16; - FALLTHROUGH_INTENDED; - case 2: - h ^= ByteAs32(data[1]) << 8; - FALLTHROUGH_INTENDED; - case 1: - h ^= ByteAs32(data[0]); - h *= m; - } - - // Do a few final mixes of the hash to ensure the last few - // bytes are well-incorporated. - h ^= h >> 13; - h *= m; - h ^= h >> 15; - return h; -} - -uint64_t Hash64(const char* data, size_t n, uint64_t seed) { - const uint64_t m = 0xc6a4a7935bd1e995; - const int r = 47; - - uint64_t h = seed ^ (n * m); - - while (n >= 8) { - uint64_t k = DecodeFixed64(data); - data += 8; - n -= 8; - - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - } - - switch (n) { - case 7: - h ^= ByteAs64(data[6]) << 48; - FALLTHROUGH_INTENDED; - case 6: - h ^= ByteAs64(data[5]) << 40; - FALLTHROUGH_INTENDED; - case 5: - h ^= ByteAs64(data[4]) << 32; - FALLTHROUGH_INTENDED; - case 4: - h ^= ByteAs64(data[3]) << 24; - FALLTHROUGH_INTENDED; - case 3: - h ^= ByteAs64(data[2]) << 16; - FALLTHROUGH_INTENDED; - case 2: - h ^= ByteAs64(data[1]) << 8; - FALLTHROUGH_INTENDED; - case 1: - h ^= ByteAs64(data[0]); - h *= m; - } - - h ^= h >> r; - h *= m; - h ^= h >> r; - - return h; -} -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/hash.h b/cmds/statsd/src/hash.h deleted file mode 100644 index bd6b0cd47eaf..000000000000 --- a/cmds/statsd/src/hash.h +++ /dev/null @@ -1,47 +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. - */ - -#pragma once - -#include <string> - -namespace android { -namespace os { -namespace statsd { - -// Uses murmur2 hashing algorithm. -extern uint32_t Hash32(const char *data, size_t n, uint32_t seed); -extern uint64_t Hash64(const char* data, size_t n, uint64_t seed); - -inline uint32_t Hash32(const char *data, size_t n) { - return Hash32(data, n, 0xBEEF); -} - -inline uint32_t Hash32(const std::string &input) { - return Hash32(input.data(), input.size()); -} - -inline uint64_t Hash64(const char* data, size_t n) { - return Hash64(data, n, 0xDECAFCAFFE); -} - -inline uint64_t Hash64(const std::string& str) { - return Hash64(str.data(), str.size()); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp deleted file mode 100644 index f56fa6221bc9..000000000000 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ /dev/null @@ -1,599 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "logd/LogEvent.h" - -#include <android-base/stringprintf.h> -#include <android/binder_ibinder.h> -#include <private/android_filesystem_config.h> - -#include "annotations.h" -#include "stats_log_util.h" -#include "statslog_statsd.h" - -namespace android { -namespace os { -namespace statsd { - -// for TrainInfo experiment id serialization -const int FIELD_ID_EXPERIMENT_ID = 1; - -using namespace android::util; -using android::base::StringPrintf; -using android::util::ProtoOutputStream; -using std::string; -using std::vector; - -// stats_event.h socket types. Keep in sync. -/* ERRORS */ -#define ERROR_NO_TIMESTAMP 0x1 -#define ERROR_NO_ATOM_ID 0x2 -#define ERROR_OVERFLOW 0x4 -#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8 -#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10 -#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20 -#define ERROR_INVALID_ANNOTATION_ID 0x40 -#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80 -#define ERROR_TOO_MANY_ANNOTATIONS 0x100 -#define ERROR_TOO_MANY_FIELDS 0x200 -#define ERROR_INVALID_VALUE_TYPE 0x400 -#define ERROR_STRING_NOT_NULL_TERMINATED 0x800 - -/* TYPE IDS */ -#define INT32_TYPE 0x00 -#define INT64_TYPE 0x01 -#define STRING_TYPE 0x02 -#define LIST_TYPE 0x03 -#define FLOAT_TYPE 0x04 -#define BOOL_TYPE 0x05 -#define BYTE_ARRAY_TYPE 0x06 -#define OBJECT_TYPE 0x07 -#define KEY_VALUE_PAIRS_TYPE 0x08 -#define ATTRIBUTION_CHAIN_TYPE 0x09 -#define ERROR_TYPE 0x0F - -LogEvent::LogEvent(int32_t uid, int32_t pid) - : mLogdTimestampNs(time(nullptr)), mLogUid(uid), mLogPid(pid) { -} - -LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging, - bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, - const std::vector<uint8_t>& experimentIds, int32_t userId) { - mLogdTimestampNs = getWallClockNs(); - mElapsedTimestampNs = getElapsedRealtimeNs(); - mTagId = util::BINARY_PUSH_STATE_CHANGED; - mLogUid = AIBinder_getCallingUid(); - mLogPid = AIBinder_getCallingPid(); - - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled))); - mValues.push_back( - FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId))); -} - -LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, - const InstallTrainInfo& trainInfo) { - mLogdTimestampNs = wallClockTimestampNs; - mElapsedTimestampNs = elapsedTimestampNs; - mTagId = util::TRAIN_INFO; - - mValues.push_back( - FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode))); - std::vector<uint8_t> experimentIdsProto; - writeExperimentIdsToProto(trainInfo.experimentIds, &experimentIdsProto); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(experimentIdsProto))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value(trainInfo.trainName))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status))); -} - -void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - int32_t value = readNextValue<int32_t>(); - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - int64_t value = readNextValue<int64_t>(); - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - int32_t numBytes = readNextValue<int32_t>(); - if ((uint32_t)numBytes > mRemainingLen) { - mValid = false; - return; - } - - string value = string((char*)mBuf, numBytes); - mBuf += numBytes; - mRemainingLen -= numBytes; - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - float value = readNextValue<float>(); - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - // cast to int32_t because FieldValue does not support bools - int32_t value = (int32_t)readNextValue<uint8_t>(); - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - int32_t numBytes = readNextValue<int32_t>(); - if ((uint32_t)numBytes > mRemainingLen) { - mValid = false; - return; - } - - vector<uint8_t> value(mBuf, mBuf + numBytes); - mBuf += numBytes; - mRemainingLen -= numBytes; - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - int32_t numPairs = readNextValue<uint8_t>(); - - for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) { - last[1] = (pos[1] == numPairs); - - // parse key - pos[2] = 1; - parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0); - - // parse value - last[2] = true; - - uint8_t typeInfo = readNextValue<uint8_t>(); - switch (getTypeId(typeInfo)) { - case INT32_TYPE: - pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto - parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0); - break; - case INT64_TYPE: - pos[2] = 3; - parseInt64(pos, /*depth=*/2, last, /*numAnnotations=*/0); - break; - case STRING_TYPE: - pos[2] = 4; - parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0); - break; - case FLOAT_TYPE: - pos[2] = 5; - parseFloat(pos, /*depth=*/2, last, /*numAnnotations=*/0); - break; - default: - mValid = false; - } - } - - parseAnnotations(numAnnotations); - - pos[1] = pos[2] = 1; - last[1] = last[2] = false; -} - -void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last, - uint8_t numAnnotations) { - const unsigned int firstUidInChainIndex = mValues.size(); - const int32_t numNodes = readNextValue<uint8_t>(); - for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) { - last[1] = (pos[1] == numNodes); - - // parse uid - pos[2] = 1; - parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0); - - // parse tag - pos[2] = 2; - last[2] = true; - parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0); - } - // Check if at least one node was successfully parsed. - if (mValues.size() - 1 > firstUidInChainIndex) { - mAttributionChainStartIndex = static_cast<int8_t>(firstUidInChainIndex); - mAttributionChainEndIndex = static_cast<int8_t>(mValues.size() - 1); - } - - parseAnnotations(numAnnotations, firstUidInChainIndex); - - pos[1] = pos[2] = 1; - last[1] = last[2] = false; -} - -// Assumes that mValues is not empty -bool LogEvent::checkPreviousValueType(Type expected) { - return mValues[mValues.size() - 1].mValue.getType() == expected; -} - -void LogEvent::parseIsUidAnnotation(uint8_t annotationType) { - if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) { - mValid = false; - return; - } - - bool isUid = readNextValue<uint8_t>(); - if (isUid) mUidFieldIndex = static_cast<int8_t>(mValues.size() - 1); - mValues[mValues.size() - 1].mAnnotations.setUidField(isUid); -} - -void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) { - if (!mValues.empty() || annotationType != BOOL_TYPE) { - mValid = false; - return; - } - - mTruncateTimestamp = readNextValue<uint8_t>(); -} - -void LogEvent::parsePrimaryFieldAnnotation(uint8_t annotationType) { - if (mValues.empty() || annotationType != BOOL_TYPE) { - mValid = false; - return; - } - - const bool primaryField = readNextValue<uint8_t>(); - mValues[mValues.size() - 1].mAnnotations.setPrimaryField(primaryField); -} - -void LogEvent::parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType, - int firstUidInChainIndex) { - if (mValues.empty() || annotationType != BOOL_TYPE || -1 == firstUidInChainIndex) { - mValid = false; - return; - } - - const bool primaryField = readNextValue<uint8_t>(); - mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(primaryField); -} - -void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) { - if (mValues.empty() || annotationType != BOOL_TYPE) { - mValid = false; - return; - } - - const bool exclusiveState = readNextValue<uint8_t>(); - mExclusiveStateFieldIndex = static_cast<int8_t>(mValues.size() - 1); - mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState); -} - -void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) { - if (mValues.empty() || annotationType != INT32_TYPE) { - mValid = false; - return; - } - - mResetState = readNextValue<int32_t>(); -} - -void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) { - if (mValues.empty() || annotationType != BOOL_TYPE) { - mValid = false; - return; - } - - bool nested = readNextValue<uint8_t>(); - mValues[mValues.size() - 1].mAnnotations.setNested(nested); -} - -// firstUidInChainIndex is a default parameter that is only needed when parsing -// annotations for attribution chains. -void LogEvent::parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex) { - for (uint8_t i = 0; i < numAnnotations; i++) { - uint8_t annotationId = readNextValue<uint8_t>(); - uint8_t annotationType = readNextValue<uint8_t>(); - - switch (annotationId) { - case ANNOTATION_ID_IS_UID: - parseIsUidAnnotation(annotationType); - break; - case ANNOTATION_ID_TRUNCATE_TIMESTAMP: - parseTruncateTimestampAnnotation(annotationType); - break; - case ANNOTATION_ID_PRIMARY_FIELD: - parsePrimaryFieldAnnotation(annotationType); - break; - case ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID: - parsePrimaryFieldFirstUidAnnotation(annotationType, firstUidInChainIndex); - break; - case ANNOTATION_ID_EXCLUSIVE_STATE: - parseExclusiveStateAnnotation(annotationType); - break; - case ANNOTATION_ID_TRIGGER_STATE_RESET: - parseTriggerStateResetAnnotation(annotationType); - break; - case ANNOTATION_ID_STATE_NESTED: - parseStateNestedAnnotation(annotationType); - break; - default: - mValid = false; - return; - } - } -} - -// This parsing logic is tied to the encoding scheme used in StatsEvent.java and -// stats_event.c -bool LogEvent::parseBuffer(uint8_t* buf, size_t len) { - mBuf = buf; - mRemainingLen = (uint32_t)len; - - int32_t pos[] = {1, 1, 1}; - bool last[] = {false, false, false}; - - // Beginning of buffer is OBJECT_TYPE | NUM_FIELDS | TIMESTAMP | ATOM_ID - uint8_t typeInfo = readNextValue<uint8_t>(); - if (getTypeId(typeInfo) != OBJECT_TYPE) mValid = false; - - uint8_t numElements = readNextValue<uint8_t>(); - if (numElements < 2 || numElements > 127) mValid = false; - - typeInfo = readNextValue<uint8_t>(); - if (getTypeId(typeInfo) != INT64_TYPE) mValid = false; - mElapsedTimestampNs = readNextValue<int64_t>(); - numElements--; - - typeInfo = readNextValue<uint8_t>(); - if (getTypeId(typeInfo) != INT32_TYPE) mValid = false; - mTagId = readNextValue<int32_t>(); - numElements--; - parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations - - for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) { - last[0] = (pos[0] == numElements); - - typeInfo = readNextValue<uint8_t>(); - uint8_t typeId = getTypeId(typeInfo); - - switch (typeId) { - case BOOL_TYPE: - parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case INT32_TYPE: - parseInt32(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case INT64_TYPE: - parseInt64(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case FLOAT_TYPE: - parseFloat(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case BYTE_ARRAY_TYPE: - parseByteArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case STRING_TYPE: - parseString(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case KEY_VALUE_PAIRS_TYPE: - parseKeyValuePairs(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case ATTRIBUTION_CHAIN_TYPE: - parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case ERROR_TYPE: - /* mErrorBitmask =*/ readNextValue<int32_t>(); - mValid = false; - break; - default: - mValid = false; - break; - } - } - - if (mRemainingLen != 0) mValid = false; - mBuf = nullptr; - return mValid; -} - -uint8_t LogEvent::getTypeId(uint8_t typeInfo) { - return typeInfo & 0x0F; // type id in lower 4 bytes -} - -uint8_t LogEvent::getNumAnnotations(uint8_t typeInfo) { - return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes -} - -int64_t LogEvent::GetLong(size_t key, status_t* err) const { - // TODO(b/110561208): encapsulate the magical operations in Field struct as static functions - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == LONG) { - return value.mValue.long_value; - } else if (value.mValue.getType() == INT) { - return value.mValue.int_value; - } else { - *err = BAD_TYPE; - return 0; - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return 0; -} - -int LogEvent::GetInt(size_t key, status_t* err) const { - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == INT) { - return value.mValue.int_value; - } else { - *err = BAD_TYPE; - return 0; - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return 0; -} - -const char* LogEvent::GetString(size_t key, status_t* err) const { - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == STRING) { - return value.mValue.str_value.c_str(); - } else { - *err = BAD_TYPE; - return 0; - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return NULL; -} - -bool LogEvent::GetBool(size_t key, status_t* err) const { - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == INT) { - return value.mValue.int_value != 0; - } else if (value.mValue.getType() == LONG) { - return value.mValue.long_value != 0; - } else { - *err = BAD_TYPE; - return false; - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return false; -} - -float LogEvent::GetFloat(size_t key, status_t* err) const { - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == FLOAT) { - return value.mValue.float_value; - } else { - *err = BAD_TYPE; - return 0.0; - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return 0.0; -} - -std::vector<uint8_t> LogEvent::GetStorage(size_t key, status_t* err) const { - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == STORAGE) { - return value.mValue.storage_value; - } else { - *err = BAD_TYPE; - return vector<uint8_t>(); - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return vector<uint8_t>(); -} - -string LogEvent::ToString() const { - string result; - result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs, - (long long)mElapsedTimestampNs, mTagId); - for (const auto& value : mValues) { - result += - StringPrintf("%#x", value.mField.getField()) + "->" + value.mValue.toString() + " "; - } - result += " }"; - return result; -} - -void LogEvent::ToProto(ProtoOutputStream& protoOutput) const { - writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput); -} - -bool LogEvent::hasAttributionChain(std::pair<int, int>* indexRange) const { - if (mAttributionChainStartIndex == -1 || mAttributionChainEndIndex == -1) { - return false; - } - - if (nullptr != indexRange) { - indexRange->first = static_cast<int>(mAttributionChainStartIndex); - indexRange->second = static_cast<int>(mAttributionChainEndIndex); - } - - return true; -} - -void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, - std::vector<uint8_t>* protoOut) { - ProtoOutputStream proto; - for (const auto& expId : experimentIds) { - proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID, - (long long)expId); - } - - protoOut->resize(proto.size()); - size_t pos = 0; - sp<ProtoReader> reader = proto.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(protoOut->data() + pos, reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h deleted file mode 100644 index a5f24603585a..000000000000 --- a/cmds/statsd/src/logd/LogEvent.h +++ /dev/null @@ -1,331 +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. - */ - -#pragma once - -#include "FieldValue.h" - -#include <android/util/ProtoOutputStream.h> -#include <private/android_logger.h> - -#include <string> -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -struct InstallTrainInfo { - int64_t trainVersionCode; - std::string trainName; - int32_t status; - std::vector<int64_t> experimentIds; - bool requiresStaging; - bool rollbackEnabled; - bool requiresLowLatencyMonitor; -}; - -/** - * This class decodes the structured, serialized encoding of an atom into a - * vector of FieldValues. - */ -class LogEvent { -public: - /** - * \param uid user id of the logging caller - * \param pid process id of the logging caller - */ - explicit LogEvent(int32_t uid, int32_t pid); - - /** - * Parses the atomId, timestamp, and vector of values from a buffer - * containing the StatsEvent/AStatsEvent encoding of an atom. - * - * \param buf a buffer that begins at the start of the serialized atom (it - * should not include the android_log_header_t or the StatsEventTag) - * \param len size of the buffer - * - * \return success of the initialization - */ - bool parseBuffer(uint8_t* buf, size_t len); - - // Constructs a BinaryPushStateChanged LogEvent from API call. - explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging, - bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, - const std::vector<uint8_t>& experimentIds, int32_t userId); - - explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, - const InstallTrainInfo& installTrainInfo); - - ~LogEvent() {} - - /** - * Get the timestamp associated with this event. - */ - inline int64_t GetLogdTimestampNs() const { return mLogdTimestampNs; } - inline int64_t GetElapsedTimestampNs() const { return mElapsedTimestampNs; } - - /** - * Get the tag for this event. - */ - inline int GetTagId() const { return mTagId; } - - /** - * Get the uid of the logging client. - * Returns -1 if the uid is unknown/has not been set. - */ - inline int32_t GetUid() const { return mLogUid; } - - /** - * Get the pid of the logging client. - * Returns -1 if the pid is unknown/has not been set. - */ - inline int32_t GetPid() const { return mLogPid; } - - /** - * Get the nth value, starting at 1. - * - * Returns BAD_INDEX if the index is larger than the number of elements. - * Returns BAD_TYPE if the index is available but the data is the wrong type. - */ - int64_t GetLong(size_t key, status_t* err) const; - int GetInt(size_t key, status_t* err) const; - const char* GetString(size_t key, status_t* err) const; - bool GetBool(size_t key, status_t* err) const; - float GetFloat(size_t key, status_t* err) const; - std::vector<uint8_t> GetStorage(size_t key, status_t* err) const; - - /** - * Return a string representation of this event. - */ - std::string ToString() const; - - /** - * Write this object to a ProtoOutputStream. - */ - void ToProto(android::util::ProtoOutputStream& out) const; - - /** - * Set elapsed timestamp if the original timestamp is missing. - */ - void setElapsedTimestampNs(int64_t timestampNs) { - mElapsedTimestampNs = timestampNs; - } - - /** - * Set the timestamp if the original logd timestamp is missing. - */ - void setLogdWallClockTimestampNs(int64_t timestampNs) { - mLogdTimestampNs = timestampNs; - } - - inline int size() const { - return mValues.size(); - } - - const std::vector<FieldValue>& getValues() const { - return mValues; - } - - std::vector<FieldValue>* getMutableValues() { - return &mValues; - } - - // Default value = false - inline bool shouldTruncateTimestamp() const { - return mTruncateTimestamp; - } - - // Returns the index of the uid field within the FieldValues vector if the - // uid exists. If there is no uid field, returns -1. - // - // If the index within the atom definition is desired, do the following: - // int vectorIndex = LogEvent.getUidFieldIndex(); - // if (vectorIndex != -1) { - // FieldValue& v = LogEvent.getValues()[vectorIndex]; - // int atomIndex = v.mField.getPosAtDepth(0); - // } - // Note that atomIndex is 1-indexed. - inline int getUidFieldIndex() { - return static_cast<int>(mUidFieldIndex); - } - - // Returns whether this LogEvent has an AttributionChain. - // If it does and indexRange is not a nullptr, populate indexRange with the start and end index - // of the AttributionChain within mValues. - bool hasAttributionChain(std::pair<int, int>* indexRange = nullptr) const; - - // Returns the index of the exclusive state field within the FieldValues vector if - // an exclusive state exists. If there is no exclusive state field, returns -1. - // - // If the index within the atom definition is desired, do the following: - // int vectorIndex = LogEvent.getExclusiveStateFieldIndex(); - // if (vectorIndex != -1) { - // FieldValue& v = LogEvent.getValues()[vectorIndex]; - // int atomIndex = v.mField.getPosAtDepth(0); - // } - // Note that atomIndex is 1-indexed. - inline int getExclusiveStateFieldIndex() const { - return static_cast<int>(mExclusiveStateFieldIndex); - } - - // If a reset state is not sent in the StatsEvent, returns -1. Note that a - // reset state is sent if and only if a reset should be triggered. - inline int getResetState() const { - return mResetState; - } - - inline LogEvent makeCopy() { - return LogEvent(*this); - } - - template <class T> - status_t updateValue(size_t key, T& value, Type type) { - int field = getSimpleField(key); - for (auto& fieldValue : mValues) { - if (fieldValue.mField.getField() == field) { - if (fieldValue.mValue.getType() == type) { - fieldValue.mValue = Value(value); - return OK; - } else { - return BAD_TYPE; - } - } - } - return BAD_INDEX; - } - - bool isValid() const { - return mValid; - } - -private: - /** - * Only use this if copy is absolutely needed. - */ - LogEvent(const LogEvent&) = default; - - void parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseAttributionChain(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - - void parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex = -1); - void parseIsUidAnnotation(uint8_t annotationType); - void parseTruncateTimestampAnnotation(uint8_t annotationType); - void parsePrimaryFieldAnnotation(uint8_t annotationType); - void parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType, int firstUidInChainIndex); - void parseExclusiveStateAnnotation(uint8_t annotationType); - void parseTriggerStateResetAnnotation(uint8_t annotationType); - void parseStateNestedAnnotation(uint8_t annotationType); - bool checkPreviousValueType(Type expected); - - /** - * The below two variables are only valid during the execution of - * parseBuffer. There are no guarantees about the state of these variables - * before/after. - */ - uint8_t* mBuf; - uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed - - bool mValid = true; // stores whether the event we received from the socket is valid - - /** - * Side-effects: - * If there is enough space in buffer to read value of type T - * - move mBuf past the value that was just read - * - decrement mRemainingLen by size of T - * Else - * - set mValid to false - */ - template <class T> - T readNextValue() { - T value; - if (mRemainingLen < sizeof(T)) { - mValid = false; - value = 0; // all primitive types can successfully cast 0 - } else { - // When alignof(T) == 1, hopefully the compiler can optimize away - // this conditional as always true. - if ((reinterpret_cast<uintptr_t>(mBuf) % alignof(T)) == 0) { - // We're properly aligned, and can safely make this assignment. - value = *((T*)mBuf); - } else { - // We need to use memcpy. It's slower, but safe. - memcpy(&value, mBuf, sizeof(T)); - } - mBuf += sizeof(T); - mRemainingLen -= sizeof(T); - } - return value; - } - - template <class T> - void addToValues(int32_t* pos, int32_t depth, T& value, bool* last) { - Field f = Field(mTagId, pos, depth); - // do not decorate last position at depth 0 - for (int i = 1; i < depth; i++) { - if (last[i]) f.decorateLastPos(i); - } - - Value v = Value(value); - mValues.push_back(FieldValue(f, v)); - } - - uint8_t getTypeId(uint8_t typeInfo); - uint8_t getNumAnnotations(uint8_t typeInfo); - - // The items are naturally sorted in DFS order as we read them. this allows us to do fast - // matching. - std::vector<FieldValue> mValues; - - // The timestamp set by the logd. - int64_t mLogdTimestampNs; - - // The elapsed timestamp set by statsd log writer. - int64_t mElapsedTimestampNs; - - // The atom tag of the event (defaults to 0 if client does not - // appropriately set the atom id). - int mTagId = 0; - - // The uid of the logging client (defaults to -1). - int32_t mLogUid = -1; - - // The pid of the logging client (defaults to -1). - int32_t mLogPid = -1; - - // Annotations - bool mTruncateTimestamp = false; - int mResetState = -1; - - // Indexes within the FieldValue vector can be stored in 7 bits because - // that's the assumption enforced by the encoding used in FieldValue. - int8_t mUidFieldIndex = -1; - int8_t mAttributionChainStartIndex = -1; - int8_t mAttributionChainEndIndex = -1; - int8_t mExclusiveStateFieldIndex = -1; -}; - -void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/logd/LogEventQueue.cpp b/cmds/statsd/src/logd/LogEventQueue.cpp deleted file mode 100644 index 146464bbe774..000000000000 --- a/cmds/statsd/src/logd/LogEventQueue.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "LogEventQueue.h" - -namespace android { -namespace os { -namespace statsd { - -using std::unique_lock; -using std::unique_ptr; - -unique_ptr<LogEvent> LogEventQueue::waitPop() { - std::unique_lock<std::mutex> lock(mMutex); - - if (mQueue.empty()) { - mCondition.wait(lock, [this] { return !this->mQueue.empty(); }); - } - - unique_ptr<LogEvent> item = std::move(mQueue.front()); - mQueue.pop(); - - return item; -} - -bool LogEventQueue::push(unique_ptr<LogEvent> item, int64_t* oldestTimestampNs) { - bool success; - { - std::unique_lock<std::mutex> lock(mMutex); - if (mQueue.size() < mQueueLimit) { - mQueue.push(std::move(item)); - success = true; - } else { - // safe operation as queue must not be empty. - *oldestTimestampNs = mQueue.front()->GetElapsedTimestampNs(); - success = false; - } - } - - mCondition.notify_one(); - return success; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/logd/LogEventQueue.h b/cmds/statsd/src/logd/LogEventQueue.h deleted file mode 100644 index 9dda3d24c571..000000000000 --- a/cmds/statsd/src/logd/LogEventQueue.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "LogEvent.h" - -#include <condition_variable> -#include <mutex> -#include <queue> - -namespace android { -namespace os { -namespace statsd { - -/** - * A zero copy thread safe queue buffer for producing and consuming LogEvent. - */ -class LogEventQueue { -public: - explicit LogEventQueue(size_t maxSize) : mQueueLimit(maxSize){}; - - /** - * Blocking read one event from the queue. - */ - std::unique_ptr<LogEvent> waitPop(); - - /** - * Puts a LogEvent ptr to the end of the queue. - * Returns false on failure when the queue is full, and output the oldest event timestamp - * in the queue. - */ - bool push(std::unique_ptr<LogEvent> event, int64_t* oldestTimestampNs); - -private: - const size_t mQueueLimit; - std::condition_variable mCondition; - std::mutex mMutex; - std::queue<std::unique_ptr<LogEvent>> mQueue; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp deleted file mode 100644 index 03b178a989eb..000000000000 --- a/cmds/statsd/src/main.cpp +++ /dev/null @@ -1,113 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsService.h" -#include "socket/StatsSocketListener.h" - -#include <android/binder_interface_utils.h> -#include <android/binder_process.h> -#include <android/binder_manager.h> -#include <utils/Looper.h> - -#include <stdio.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -using namespace android; -using namespace android::os::statsd; -using ::ndk::SharedRefBase; -using std::shared_ptr; -using std::make_shared; - -shared_ptr<StatsService> gStatsService = nullptr; -sp<StatsSocketListener> gSocketListener = nullptr; - -void signalHandler(int sig) { - if (sig == SIGPIPE) { - // ShellSubscriber uses SIGPIPE as a signal to detect the end of the - // client process. Don't prematurely exit(1) here. Instead, ignore the - // signal and allow the write call to return EPIPE. - ALOGI("statsd received SIGPIPE. Ignoring signal."); - return; - } - - if (gSocketListener != nullptr) gSocketListener->stopListener(); - if (gStatsService != nullptr) gStatsService->Terminate(); - ALOGW("statsd terminated on receiving signal %d.", sig); - exit(1); -} - -void registerSignalHandlers() -{ - struct sigaction sa; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = signalHandler; - sigaction(SIGPIPE, &sa, nullptr); - sigaction(SIGHUP, &sa, nullptr); - sigaction(SIGINT, &sa, nullptr); - sigaction(SIGQUIT, &sa, nullptr); - sigaction(SIGTERM, &sa, nullptr); -} - -int main(int /*argc*/, char** /*argv*/) { - // Set up the looper - sp<Looper> looper(Looper::prepare(0 /* opts */)); - - // Set up the binder - ABinderProcess_setThreadPoolMaxThreadCount(9); - ABinderProcess_startThreadPool(); - - std::shared_ptr<LogEventQueue> eventQueue = - std::make_shared<LogEventQueue>(4000 /*buffer limit. Buffer is NOT pre-allocated*/); - - // Create the service - gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue); - // TODO(b/149582373): Set DUMP_FLAG_PROTO once libbinder_ndk supports - // setting dumpsys priorities. - binder_status_t status = AServiceManager_addService(gStatsService->asBinder().get(), "stats"); - if (status != STATUS_OK) { - ALOGE("Failed to add service as AIDL service"); - return -1; - } - - registerSignalHandlers(); - - gStatsService->sayHiToStatsCompanion(); - - gStatsService->Startup(); - - gSocketListener = new StatsSocketListener(eventQueue); - - ALOGI("Statsd starts to listen to socket."); - // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value - if (gSocketListener->startListener(600)) { - exit(1); - } - - // Loop forever -- the reports run on this thread in a handler, and the - // binder calls remain responsive in their pool of one thread. - while (true) { - looper->pollAll(-1 /* timeoutMillis */); - } - ALOGW("statsd escaped from its loop."); - - return 1; -} diff --git a/cmds/statsd/src/matchers/AtomMatchingTracker.h b/cmds/statsd/src/matchers/AtomMatchingTracker.h deleted file mode 100644 index c1384972464c..000000000000 --- a/cmds/statsd/src/matchers/AtomMatchingTracker.h +++ /dev/null @@ -1,118 +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. - */ - -#ifndef ATOM_MATCHING_TRACKER_H -#define ATOM_MATCHING_TRACKER_H - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "logd/LogEvent.h" -#include "matchers/matcher_util.h" - -#include <utils/RefBase.h> - -#include <set> -#include <unordered_map> -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -class AtomMatchingTracker : public virtual RefBase { -public: - AtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash) - : mId(id), mIndex(index), mInitialized(false), mProtoHash(protoHash){}; - - virtual ~AtomMatchingTracker(){}; - - // Initialize this AtomMatchingTracker. - // allAtomMatchers: the list of the AtomMatcher proto config. This is needed because we don't - // store the proto object in memory. We only need it during initilization. - // allAtomMatchingTrackers: the list of the AtomMatchingTracker objects. It's a one-to-one - // mapping with allAtomMatchers. This is needed because the - // initialization is done recursively for - // CombinationAtomMatchingTrackers using DFS. - // stack: a bit map to record which matcher has been visited on the stack. This is for detecting - // circle dependency. - virtual bool init(const std::vector<AtomMatcher>& allAtomMatchers, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& matcherMap, - std::vector<bool>& stack) = 0; - - // Update appropriate state on config updates. Primarily, all indices need to be updated. - // This matcher and all of its children are guaranteed to be preserved across the update. - // matcher: the AtomMatcher proto from the config. - // index: the index of this matcher in mAllAtomMatchingTrackers. - // atomMatchingTrackerMap: map from matcher id to index in mAllAtomMatchingTrackers - virtual bool onConfigUpdated( - const AtomMatcher& matcher, const int index, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) = 0; - - // Called when a log event comes. - // event: the log event. - // allAtomMatchingTrackers: the list of all AtomMatchingTrackers. This is needed because the log - // processing is done recursively. - // matcherResults: The cached results for all matchers for this event. Parent matchers can - // directly access the children's matching results if they have been evaluated. - // Otherwise, call children matchers' onLogEvent. - virtual void onLogEvent(const LogEvent& event, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - std::vector<MatchingState>& matcherResults) = 0; - - // Get the tagIds that this matcher cares about. The combined collection is stored - // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses - // some memory but hopefully it can save us much CPU time when there is flood of events. - virtual const std::set<int>& getAtomIds() const { - return mAtomIds; - } - - int64_t getId() const { - return mId; - } - - uint64_t getProtoHash() const { - return mProtoHash; - } - -protected: - // Name of this matching. We don't really need the name, but it makes log message easy to debug. - const int64_t mId; - - // Index of this AtomMatchingTracker in MetricsManager's container. - int mIndex; - - // Whether this AtomMatchingTracker has been properly initialized. - bool mInitialized; - - // The collection of the event tag ids that this AtomMatchingTracker cares. So we can quickly - // return kNotMatched when we receive an event with an id not in the list. This is especially - // useful when we have a complex CombinationAtomMatchingTracker. - std::set<int> mAtomIds; - - // Hash of the AtomMatcher's proto bytes from StatsdConfig. - // Used to determine if the definition of this matcher has changed across a config update. - const uint64_t mProtoHash; - - FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple); - FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination); - FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // ATOM_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp deleted file mode 100644 index 45685ce5bfee..000000000000 --- a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp +++ /dev/null @@ -1,140 +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. - */ - -#include "Log.h" - -#include "CombinationAtomMatchingTracker.h" - -#include "matchers/matcher_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::set; -using std::unordered_map; -using std::vector; - -CombinationAtomMatchingTracker::CombinationAtomMatchingTracker(const int64_t& id, const int index, - const uint64_t protoHash) - : AtomMatchingTracker(id, index, protoHash) { -} - -CombinationAtomMatchingTracker::~CombinationAtomMatchingTracker() { -} - -bool CombinationAtomMatchingTracker::init( - const vector<AtomMatcher>& allAtomMatchers, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) { - if (mInitialized) { - return true; - } - - // mark this node as visited in the recursion stack. - stack[mIndex] = true; - - AtomMatcher_Combination matcher = allAtomMatchers[mIndex].combination(); - - // LogicalOperation is missing in the config - if (!matcher.has_operation()) { - return false; - } - - mLogicalOperation = matcher.operation(); - - if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) { - return false; - } - - for (const auto& child : matcher.matcher()) { - auto pair = matcherMap.find(child); - if (pair == matcherMap.end()) { - ALOGW("Matcher %lld not found in the config", (long long)child); - return false; - } - - int childIndex = pair->second; - - // if the child is a visited node in the recursion -> circle detected. - if (stack[childIndex]) { - ALOGE("Circle detected in matcher config"); - return false; - } - - if (!allAtomMatchingTrackers[childIndex]->init(allAtomMatchers, allAtomMatchingTrackers, - matcherMap, stack)) { - ALOGW("child matcher init failed %lld", (long long)child); - return false; - } - - mChildren.push_back(childIndex); - - const set<int>& childTagIds = allAtomMatchingTrackers[childIndex]->getAtomIds(); - mAtomIds.insert(childTagIds.begin(), childTagIds.end()); - } - - mInitialized = true; - // unmark this node in the recursion stack. - stack[mIndex] = false; - return true; -} - -bool CombinationAtomMatchingTracker::onConfigUpdated( - const AtomMatcher& matcher, const int index, - const unordered_map<int64_t, int>& atomMatchingTrackerMap) { - mIndex = index; - mChildren.clear(); - AtomMatcher_Combination combinationMatcher = matcher.combination(); - for (const int64_t child : combinationMatcher.matcher()) { - const auto& pair = atomMatchingTrackerMap.find(child); - if (pair == atomMatchingTrackerMap.end()) { - ALOGW("Matcher %lld not found in the config", (long long)child); - return false; - } - mChildren.push_back(pair->second); - } - return true; -} - -void CombinationAtomMatchingTracker::onLogEvent( - const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - vector<MatchingState>& matcherResults) { - // this event has been processed. - if (matcherResults[mIndex] != MatchingState::kNotComputed) { - return; - } - - if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) { - matcherResults[mIndex] = MatchingState::kNotMatched; - return; - } - - // evaluate children matchers if they haven't been evaluated. - for (const int childIndex : mChildren) { - if (matcherResults[childIndex] == MatchingState::kNotComputed) { - const sp<AtomMatchingTracker>& child = allAtomMatchingTrackers[childIndex]; - child->onLogEvent(event, allAtomMatchingTrackers, matcherResults); - } - } - - bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults); - matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h deleted file mode 100644 index 3160448b6c76..000000000000 --- a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h +++ /dev/null @@ -1,58 +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. - */ -#ifndef COMBINATION_ATOM_MATCHING_TRACKER_H -#define COMBINATION_ATOM_MATCHING_TRACKER_H - -#include <unordered_map> -#include <vector> - -#include "AtomMatchingTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -namespace android { -namespace os { -namespace statsd { - -// Represents a AtomMatcher_Combination in the StatsdConfig. -class CombinationAtomMatchingTracker : public AtomMatchingTracker { -public: - CombinationAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash); - - bool init(const std::vector<AtomMatcher>& allAtomMatchers, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack); - - bool onConfigUpdated(const AtomMatcher& matcher, const int index, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override; - - ~CombinationAtomMatchingTracker(); - - void onLogEvent(const LogEvent& event, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - std::vector<MatchingState>& matcherResults) override; - -private: - LogicalOperation mLogicalOperation; - - std::vector<int> mChildren; - - FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers); -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // COMBINATION_ATOM_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.cpp b/cmds/statsd/src/matchers/EventMatcherWizard.cpp deleted file mode 100644 index 025c9a87b16b..000000000000 --- a/cmds/statsd/src/matchers/EventMatcherWizard.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - */ -#include "EventMatcherWizard.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -MatchingState EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcher_index) { - if (matcher_index < 0 || matcher_index >= (int)mAllEventMatchers.size()) { - return MatchingState::kNotComputed; - } - vector<MatchingState> matcherCache(mAllEventMatchers.size(), MatchingState::kNotComputed); - mAllEventMatchers[matcher_index]->onLogEvent(event, mAllEventMatchers, matcherCache); - return matcherCache[matcher_index]; -} - -} // namespace statsd -} // namespace os -} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.h b/cmds/statsd/src/matchers/EventMatcherWizard.h deleted file mode 100644 index 5d780f2423d8..000000000000 --- a/cmds/statsd/src/matchers/EventMatcherWizard.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include "AtomMatchingTracker.h" - -namespace android { -namespace os { -namespace statsd { - -class EventMatcherWizard : public virtual android::RefBase { -public: - EventMatcherWizard(){}; // for testing - EventMatcherWizard(const std::vector<sp<AtomMatchingTracker>>& eventTrackers) - : mAllEventMatchers(eventTrackers){}; - - virtual ~EventMatcherWizard(){}; - - MatchingState matchLogEvent(const LogEvent& event, int matcher_index); - -private: - std::vector<sp<AtomMatchingTracker>> mAllEventMatchers; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp deleted file mode 100644 index 423da5bd3cf8..000000000000 --- a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp +++ /dev/null @@ -1,81 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "SimpleAtomMatchingTracker.h" - -namespace android { -namespace os { -namespace statsd { - -using std::unordered_map; -using std::vector; - -SimpleAtomMatchingTracker::SimpleAtomMatchingTracker(const int64_t& id, const int index, - const uint64_t protoHash, - const SimpleAtomMatcher& matcher, - const sp<UidMap>& uidMap) - : AtomMatchingTracker(id, index, protoHash), mMatcher(matcher), mUidMap(uidMap) { - if (!matcher.has_atom_id()) { - mInitialized = false; - } else { - mAtomIds.insert(matcher.atom_id()); - mInitialized = true; - } -} - -SimpleAtomMatchingTracker::~SimpleAtomMatchingTracker() { -} - -bool SimpleAtomMatchingTracker::init(const vector<AtomMatcher>& allAtomMatchers, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& matcherMap, - vector<bool>& stack) { - // no need to do anything. - return mInitialized; -} - -bool SimpleAtomMatchingTracker::onConfigUpdated( - const AtomMatcher& matcher, const int index, - const unordered_map<int64_t, int>& atomMatchingTrackerMap) { - mIndex = index; - // Do not need to update mMatcher since the matcher must be identical across the update. - return mInitialized; -} - -void SimpleAtomMatchingTracker::onLogEvent( - const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - vector<MatchingState>& matcherResults) { - if (matcherResults[mIndex] != MatchingState::kNotComputed) { - VLOG("Matcher %lld already evaluated ", (long long)mId); - return; - } - - if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) { - matcherResults[mIndex] = MatchingState::kNotMatched; - return; - } - - bool matched = matchesSimple(mUidMap, mMatcher, event); - matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched; - VLOG("Stats SimpleAtomMatcher %lld matched? %d", (long long)mId, matched); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h deleted file mode 100644 index b67e6c20e8f1..000000000000 --- a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h +++ /dev/null @@ -1,58 +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. - */ - -#ifndef SIMPLE_ATOM_MATCHING_TRACKER_H -#define SIMPLE_ATOM_MATCHING_TRACKER_H - -#include <unordered_map> -#include <vector> - -#include "AtomMatchingTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "packages/UidMap.h" - -namespace android { -namespace os { -namespace statsd { - -class SimpleAtomMatchingTracker : public AtomMatchingTracker { -public: - SimpleAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash, - const SimpleAtomMatcher& matcher, const sp<UidMap>& uidMap); - - ~SimpleAtomMatchingTracker(); - - bool init(const std::vector<AtomMatcher>& allAtomMatchers, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& matcherMap, - std::vector<bool>& stack) override; - - bool onConfigUpdated(const AtomMatcher& matcher, const int index, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override; - - void onLogEvent(const LogEvent& event, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - std::vector<MatchingState>& matcherResults) override; - -private: - const SimpleAtomMatcher mMatcher; - const sp<UidMap> mUidMap; -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // SIMPLE_ATOM_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp deleted file mode 100644 index a7454c5d923b..000000000000 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ /dev/null @@ -1,373 +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. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/AtomMatchingTracker.h" -#include "matchers/matcher_util.h" -#include "stats_util.h" - -using std::set; -using std::string; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -bool combinationMatch(const vector<int>& children, const LogicalOperation& operation, - const vector<MatchingState>& matcherResults) { - bool matched; - switch (operation) { - case LogicalOperation::AND: { - matched = true; - for (const int childIndex : children) { - if (matcherResults[childIndex] != MatchingState::kMatched) { - matched = false; - break; - } - } - break; - } - case LogicalOperation::OR: { - matched = false; - for (const int childIndex : children) { - if (matcherResults[childIndex] == MatchingState::kMatched) { - matched = true; - break; - } - } - break; - } - case LogicalOperation::NOT: - matched = matcherResults[children[0]] == MatchingState::kNotMatched; - break; - case LogicalOperation::NAND: - matched = false; - for (const int childIndex : children) { - if (matcherResults[childIndex] != MatchingState::kMatched) { - matched = true; - break; - } - } - break; - case LogicalOperation::NOR: - matched = true; - for (const int childIndex : children) { - if (matcherResults[childIndex] == MatchingState::kMatched) { - matched = false; - break; - } - } - break; - case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED: - matched = false; - break; - } - return matched; -} - -bool tryMatchString(const sp<UidMap>& uidMap, const FieldValue& fieldValue, - const string& str_match) { - if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) { - int uid = fieldValue.mValue.int_value; - auto aidIt = UidMap::sAidToUidMapping.find(str_match); - if (aidIt != UidMap::sAidToUidMapping.end()) { - return ((int)aidIt->second) == uid; - } - std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/); - return packageNames.find(str_match) != packageNames.end(); - } else if (fieldValue.mValue.getType() == STRING) { - return fieldValue.mValue.str_value == str_match; - } - return false; -} - -bool matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher, - const vector<FieldValue>& values, int start, int end, int depth) { - if (depth > 2) { - ALOGE("Depth > 3 not supported"); - return false; - } - - if (start >= end) { - return false; - } - - // Filter by entry field first - int newStart = -1; - int newEnd = end; - // because the fields are naturally sorted in the DFS order. we can safely - // break when pos is larger than the one we are searching for. - for (int i = start; i < end; i++) { - int pos = values[i].mField.getPosAtDepth(depth); - if (pos == matcher.field()) { - if (newStart == -1) { - newStart = i; - } - newEnd = i + 1; - } else if (pos > matcher.field()) { - break; - } - } - - // Now we have zoomed in to a new range - start = newStart; - end = newEnd; - - if (start == -1) { - // No such field found. - return false; - } - - vector<pair<int, int>> ranges; // the ranges are for matching ANY position - if (matcher.has_position()) { - // Repeated fields position is stored as a node in the path. - depth++; - if (depth > 2) { - return false; - } - switch (matcher.position()) { - case Position::FIRST: { - for (int i = start; i < end; i++) { - int pos = values[i].mField.getPosAtDepth(depth); - if (pos != 1) { - // Again, the log elements are stored in sorted order. so - // once the position is > 1, we break; - end = i; - break; - } - } - ranges.push_back(std::make_pair(start, end)); - break; - } - case Position::LAST: { - // move the starting index to the first LAST field at the depth. - for (int i = start; i < end; i++) { - if (values[i].mField.isLastPos(depth)) { - start = i; - break; - } - } - ranges.push_back(std::make_pair(start, end)); - break; - } - case Position::ANY: { - // ANY means all the children matchers match in any of the sub trees, it's a match - newStart = start; - newEnd = end; - // Here start is guaranteed to be a valid index. - int currentPos = values[start].mField.getPosAtDepth(depth); - // Now find all sub trees ranges. - for (int i = start; i < end; i++) { - int newPos = values[i].mField.getPosAtDepth(depth); - if (newPos != currentPos) { - ranges.push_back(std::make_pair(newStart, i)); - newStart = i; - currentPos = newPos; - } - } - ranges.push_back(std::make_pair(newStart, end)); - break; - } - case Position::ALL: - ALOGE("Not supported: field matcher with ALL position."); - break; - case Position::POSITION_UNKNOWN: - break; - } - } else { - // No position - ranges.push_back(std::make_pair(start, end)); - } - // start and end are still pointing to the matched range. - switch (matcher.value_matcher_case()) { - case FieldValueMatcher::kMatchesTuple: { - ++depth; - // If any range matches all matchers, good. - for (const auto& range : ranges) { - bool matched = true; - for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) { - if (!matchesSimple(uidMap, subMatcher, values, range.first, range.second, - depth)) { - matched = false; - break; - } - } - if (matched) return true; - } - return false; - } - // Finally, we get to the point of real value matching. - // If the field matcher ends with ANY, then we have [start, end) range > 1. - // In the following, we should return true, when ANY of the values matches. - case FieldValueMatcher::ValueMatcherCase::kEqBool: { - for (int i = start; i < end; i++) { - if ((values[i].mValue.getType() == INT && - (values[i].mValue.int_value != 0) == matcher.eq_bool()) || - (values[i].mValue.getType() == LONG && - (values[i].mValue.long_value != 0) == matcher.eq_bool())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kEqString: { - for (int i = start; i < end; i++) { - if (tryMatchString(uidMap, values[i], matcher.eq_string())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kNeqAnyString: { - const auto& str_list = matcher.neq_any_string(); - for (int i = start; i < end; i++) { - bool notEqAll = true; - for (const auto& str : str_list.str_value()) { - if (tryMatchString(uidMap, values[i], str)) { - notEqAll = false; - break; - } - } - if (notEqAll) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kEqAnyString: { - const auto& str_list = matcher.eq_any_string(); - for (int i = start; i < end; i++) { - for (const auto& str : str_list.str_value()) { - if (tryMatchString(uidMap, values[i], str)) { - return true; - } - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kEqInt: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == INT && - (matcher.eq_int() == values[i].mValue.int_value)) { - return true; - } - // eq_int covers both int and long. - if (values[i].mValue.getType() == LONG && - (matcher.eq_int() == values[i].mValue.long_value)) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kLtInt: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == INT && - (values[i].mValue.int_value < matcher.lt_int())) { - return true; - } - // lt_int covers both int and long. - if (values[i].mValue.getType() == LONG && - (values[i].mValue.long_value < matcher.lt_int())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kGtInt: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == INT && - (values[i].mValue.int_value > matcher.gt_int())) { - return true; - } - // gt_int covers both int and long. - if (values[i].mValue.getType() == LONG && - (values[i].mValue.long_value > matcher.gt_int())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kLtFloat: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == FLOAT && - (values[i].mValue.float_value < matcher.lt_float())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kGtFloat: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == FLOAT && - (values[i].mValue.float_value > matcher.gt_float())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kLteInt: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == INT && - (values[i].mValue.int_value <= matcher.lte_int())) { - return true; - } - // lte_int covers both int and long. - if (values[i].mValue.getType() == LONG && - (values[i].mValue.long_value <= matcher.lte_int())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kGteInt: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == INT && - (values[i].mValue.int_value >= matcher.gte_int())) { - return true; - } - // gte_int covers both int and long. - if (values[i].mValue.getType() == LONG && - (values[i].mValue.long_value >= matcher.gte_int())) { - return true; - } - } - return false; - } - default: - return false; - } -} - -bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher, - const LogEvent& event) { - if (event.GetTagId() != simpleMatcher.atom_id()) { - return false; - } - - for (const auto& matcher : simpleMatcher.field_value_matcher()) { - if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) { - return false; - } - } - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h deleted file mode 100644 index 130b6068bd19..000000000000 --- a/cmds/statsd/src/matchers/matcher_util.h +++ /dev/null @@ -1,44 +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. - */ - -#pragma once - -#include "logd/LogEvent.h" - -#include <vector> -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "packages/UidMap.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -enum MatchingState { - kNotComputed = -1, - kNotMatched = 0, - kMatched = 1, -}; - -bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation, - const std::vector<MatchingState>& matcherResults); - -bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher, - const LogEvent& wrapper); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metadata_util.cpp b/cmds/statsd/src/metadata_util.cpp deleted file mode 100644 index 27ee59b36242..000000000000 --- a/cmds/statsd/src/metadata_util.cpp +++ /dev/null @@ -1,122 +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. - */ - -#include "FieldValue.h" -#include "metadata_util.h" - -namespace android { -namespace os { -namespace statsd { - -using google::protobuf::RepeatedPtrField; - -void writeValueToProto(metadata::FieldValue* metadataFieldValue, const Value& value) { - std::string storage_value; - switch (value.getType()) { - case INT: - metadataFieldValue->set_value_int(value.int_value); - break; - case LONG: - metadataFieldValue->set_value_long(value.long_value); - break; - case FLOAT: - metadataFieldValue->set_value_float(value.float_value); - break; - case DOUBLE: - metadataFieldValue->set_value_double(value.double_value); - break; - case STRING: - metadataFieldValue->set_value_str(value.str_value.c_str()); - break; - case STORAGE: // byte array - storage_value = ((char*) value.storage_value.data()); - metadataFieldValue->set_value_storage(storage_value); - break; - default: - break; - } -} - -void writeMetricDimensionKeyToMetadataDimensionKey( - const MetricDimensionKey& metricKey, - metadata::MetricDimensionKey* metadataMetricKey) { - for (const FieldValue& fieldValue : metricKey.getDimensionKeyInWhat().getValues()) { - metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_dimension_key_in_what(); - metadata::Field* metadataField = metadataFieldValue->mutable_field(); - metadataField->set_tag(fieldValue.mField.getTag()); - metadataField->set_field(fieldValue.mField.getField()); - writeValueToProto(metadataFieldValue, fieldValue.mValue); - } - - for (const FieldValue& fieldValue : metricKey.getStateValuesKey().getValues()) { - metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_state_values_key(); - metadata::Field* metadataField = metadataFieldValue->mutable_field(); - metadataField->set_tag(fieldValue.mField.getTag()); - metadataField->set_field(fieldValue.mField.getField()); - writeValueToProto(metadataFieldValue, fieldValue.mValue); - } -} - -void writeFieldValuesFromMetadata( - const RepeatedPtrField<metadata::FieldValue>& repeatedFieldValueList, - std::vector<FieldValue>* fieldValues) { - for (const metadata::FieldValue& metadataFieldValue : repeatedFieldValueList) { - Field field(metadataFieldValue.field().tag(), metadataFieldValue.field().field()); - Value value; - switch (metadataFieldValue.value_case()) { - case metadata::FieldValue::ValueCase::kValueInt: - value = Value(metadataFieldValue.value_int()); - break; - case metadata::FieldValue::ValueCase::kValueLong: - value = Value(metadataFieldValue.value_long()); - break; - case metadata::FieldValue::ValueCase::kValueFloat: - value = Value(metadataFieldValue.value_float()); - break; - case metadata::FieldValue::ValueCase::kValueDouble: - value = Value(metadataFieldValue.value_double()); - break; - case metadata::FieldValue::ValueCase::kValueStr: - value = Value(metadataFieldValue.value_str()); - break; - case metadata::FieldValue::ValueCase::kValueStorage: - value = Value(metadataFieldValue.value_storage()); - break; - default: - break; - } - FieldValue fieldValue(field, value); - fieldValues->emplace_back(field, value); - } -} - -MetricDimensionKey loadMetricDimensionKeyFromProto( - const metadata::MetricDimensionKey& metricDimensionKey) { - std::vector<FieldValue> dimKeyInWhatFieldValues; - writeFieldValuesFromMetadata(metricDimensionKey.dimension_key_in_what(), - &dimKeyInWhatFieldValues); - std::vector<FieldValue> stateValuesFieldValues; - writeFieldValuesFromMetadata(metricDimensionKey.state_values_key(), &stateValuesFieldValues); - - HashableDimensionKey dimKeyInWhat(dimKeyInWhatFieldValues); - HashableDimensionKey stateValues(stateValuesFieldValues); - MetricDimensionKey metricKey(dimKeyInWhat, stateValues); - return metricKey; -} - -} // namespace statsd -} // namespace os -} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/metadata_util.h b/cmds/statsd/src/metadata_util.h deleted file mode 100644 index 84a39ff872b5..000000000000 --- a/cmds/statsd/src/metadata_util.h +++ /dev/null @@ -1,32 +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. - */ -#include "HashableDimensionKey.h" - -#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata - -namespace android { -namespace os { -namespace statsd { - -void writeMetricDimensionKeyToMetadataDimensionKey(const MetricDimensionKey& metricKey, - metadata::MetricDimensionKey* metadataMetricKey); - -MetricDimensionKey loadMetricDimensionKeyFromProto( - const metadata::MetricDimensionKey& metricDimensionKey); - -} // namespace statsd -} // namespace os -} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp deleted file mode 100644 index a8ef54a335c4..000000000000 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ /dev/null @@ -1,440 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "CountMetricProducer.h" - -#include <inttypes.h> -#include <limits.h> -#include <stdlib.h> - -#include "guardrail/StatsdStats.h" -#include "metrics/parsing_utils/metrics_manager_util.h" -#include "stats_log_util.h" -#include "stats_util.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::map; -using std::string; -using std::unordered_map; -using std::vector; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -// for StatsLogReport -const int FIELD_ID_ID = 1; -const int FIELD_ID_COUNT_METRICS = 5; -const int FIELD_ID_TIME_BASE = 9; -const int FIELD_ID_BUCKET_SIZE = 10; -const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; -const int FIELD_ID_IS_ACTIVE = 14; - -// for CountMetricDataWrapper -const int FIELD_ID_DATA = 1; -// for CountMetricData -const int FIELD_ID_DIMENSION_IN_WHAT = 1; -const int FIELD_ID_SLICE_BY_STATE = 6; -const int FIELD_ID_BUCKET_INFO = 3; -const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4; -// for CountBucketInfo -const int FIELD_ID_COUNT = 3; -const int FIELD_ID_BUCKET_NUM = 4; -const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5; -const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; - -CountMetricProducer::CountMetricProducer( - const ConfigKey& key, const CountMetric& metric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs, - const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, - const vector<int>& slicedStateAtoms, - const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, - protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms, - stateGroupMap) { - if (metric.has_bucket()) { - mBucketSizeNs = - TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; - } else { - mBucketSizeNs = LLONG_MAX; - } - - if (metric.has_dimensions_in_what()) { - translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); - mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); - } - - mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()); - - if (metric.links().size() > 0) { - for (const auto& link : metric.links()) { - Metric2Condition mc; - mc.conditionId = link.condition(); - translateFieldMatcher(link.fields_in_what(), &mc.metricFields); - translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); - mMetric2ConditionLinks.push_back(mc); - } - mConditionSliced = true; - } - - for (const auto& stateLink : metric.state_link()) { - Metric2State ms; - ms.stateAtomId = stateLink.state_atom_id(); - translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields); - translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields); - mMetric2StateLinks.push_back(ms); - } - - flushIfNeededLocked(startTimeNs); - // Adjust start for partial bucket - mCurrentBucketStartTimeNs = startTimeNs; - - VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), - (long long)mBucketSizeNs, (long long)mTimeBaseNs); -} - -CountMetricProducer::~CountMetricProducer() { - VLOG("~CountMetricProducer() called"); -} - -bool CountMetricProducer::onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - if (!MetricProducer::onConfigUpdatedLocked( - config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return false; - } - - const CountMetric& metric = config.count_metric(configIndex); - int trackerIndex; - // Update appropriate indices, specifically mConditionIndex and MetricsManager maps. - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { - return false; - } - - if (metric.has_condition() && - !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, mConditionTrackerIndex, - conditionToMetricMap)) { - return false; - } - return true; -} - -void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, - const FieldValue& oldState, const FieldValue& newState) { - VLOG("CountMetric %lld onStateChanged time %lld, State%d, key %s, %d -> %d", - (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(), - oldState.mValue.int_value, newState.mValue.int_value); -} - -void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { - if (mCurrentSlicedCounter == nullptr || - mCurrentSlicedCounter->size() == 0) { - return; - } - - fprintf(out, "CountMetric %lld dimension size %lu\n", (long long)mMetricId, - (unsigned long)mCurrentSlicedCounter->size()); - if (verbose) { - for (const auto& it : *mCurrentSlicedCounter) { - fprintf(out, "\t(what)%s\t(state)%s %lld\n", - it.first.getDimensionKeyInWhat().toString().c_str(), - it.first.getStateValuesKey().toString().c_str(), (unsigned long long)it.second); - } - } -} - -void CountMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) { - VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); -} - - -void CountMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - mPastBuckets.clear(); -} - -void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - ProtoOutputStream* protoOutput) { - if (include_current_partial_bucket) { - flushLocked(dumpTimeNs); - } else { - flushIfNeededLocked(dumpTimeNs); - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); - - - if (mPastBuckets.empty()) { - return; - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs); - - // Fills the dimension path if not slicing by ALL. - if (!mSliceByPositionALL) { - if (!mDimensionsInWhat.empty()) { - uint64_t dimenPathToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT); - writeDimensionPathToProto(mDimensionsInWhat, protoOutput); - protoOutput->end(dimenPathToken); - } - } - - uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS); - - for (const auto& counter : mPastBuckets) { - const MetricDimensionKey& dimensionKey = counter.first; - VLOG(" dimension key %s", dimensionKey.toString().c_str()); - - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - - // First fill dimension. - if (mSliceByPositionALL) { - uint64_t dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput); - protoOutput->end(dimensionToken); - } else { - writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(), - FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput); - } - // Then fill slice_by_state. - for (auto state : dimensionKey.getStateValuesKey().getValues()) { - uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SLICE_BY_STATE); - writeStateToProto(state, protoOutput); - protoOutput->end(stateToken); - } - // Then fill bucket_info (CountBucketInfo). - for (const auto& bucket : counter.second) { - uint64_t bucketInfoToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - // Partial bucket. - if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketStartNs)); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketEndNs)); - } else { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM, - (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs))); - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount); - protoOutput->end(bucketInfoToken); - VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs, - (long long)bucket.mBucketEndNs, (long long)bucket.mCount); - } - protoOutput->end(wrapperToken); - } - - protoOutput->end(protoToken); - - if (erase_data) { - mPastBuckets.clear(); - } -} - -void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - flushIfNeededLocked(dropTimeNs); - StatsdStats::getInstance().noteBucketDropped(mMetricId); - mPastBuckets.clear(); -} - -void CountMetricProducer::onConditionChangedLocked(const bool conditionMet, - const int64_t eventTime) { - VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; -} - -bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { - if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) { - return false; - } - // ===========GuardRail============== - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mCurrentSlicedCounter->size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("CountMetric %lld dropping data for dimension key %s", - (long long)mMetricId, newKey.toString().c_str()); - StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); - return true; - } - } - - return false; -} - -void CountMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const map<int, HashableDimensionKey>& statePrimaryKeys) { - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - flushIfNeededLocked(eventTimeNs); - - if (!condition) { - return; - } - - auto it = mCurrentSlicedCounter->find(eventKey); - if (it == mCurrentSlicedCounter->end()) { - // ===========GuardRail============== - if (hitGuardRailLocked(eventKey)) { - return; - } - // create a counter for the new key - (*mCurrentSlicedCounter)[eventKey] = 1; - } else { - // increment the existing value - auto& count = it->second; - count++; - } - for (auto& tracker : mAnomalyTrackers) { - int64_t countWholeBucket = mCurrentSlicedCounter->find(eventKey)->second; - auto prev = mCurrentFullCounters->find(eventKey); - if (prev != mCurrentFullCounters->end()) { - countWholeBucket += prev->second; - } - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey, - countWholeBucket); - } - - VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.toString().c_str(), - (long long)(*mCurrentSlicedCounter)[eventKey]); -} - -// When a new matched event comes in, we check if event falls into the current -// bucket. If not, flush the old counter to past buckets and initialize the new bucket. -void CountMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { - int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - if (eventTimeNs < currentBucketEndTimeNs) { - return; - } - - // Setup the bucket start time and number. - int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; - int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; - flushCurrentBucketLocked(eventTimeNs, nextBucketNs); - - mCurrentBucketNum += numBucketsForward; - VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, - (long long)mCurrentBucketStartTimeNs); -} - -void CountMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) { - int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); - CountBucket info; - info.mBucketStartNs = mCurrentBucketStartTimeNs; - if (eventTimeNs < fullBucketEndTimeNs) { - info.mBucketEndNs = eventTimeNs; - } else { - info.mBucketEndNs = fullBucketEndTimeNs; - } - for (const auto& counter : *mCurrentSlicedCounter) { - info.mCount = counter.second; - auto& bucketList = mPastBuckets[counter.first]; - bucketList.push_back(info); - VLOG("metric %lld, dump key value: %s -> %lld", (long long)mMetricId, - counter.first.toString().c_str(), - (long long)counter.second); - } - - // If we have finished a full bucket, then send this to anomaly tracker. - if (eventTimeNs > fullBucketEndTimeNs) { - // Accumulate partial buckets with current value and then send to anomaly tracker. - if (mCurrentFullCounters->size() > 0) { - for (const auto& keyValuePair : *mCurrentSlicedCounter) { - (*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second; - } - for (auto& tracker : mAnomalyTrackers) { - tracker->addPastBucket(mCurrentFullCounters, mCurrentBucketNum); - } - mCurrentFullCounters = std::make_shared<DimToValMap>(); - } else { - // Skip aggregating the partial buckets since there's no previous partial bucket. - for (auto& tracker : mAnomalyTrackers) { - tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum); - } - } - } else { - // Accumulate partial bucket. - for (const auto& keyValuePair : *mCurrentSlicedCounter) { - (*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second; - } - } - - StatsdStats::getInstance().noteBucketCount(mMetricId); - // Only resets the counters, but doesn't setup the times nor numbers. - // (Do not clear since the old one is still referenced in mAnomalyTrackers). - mCurrentSlicedCounter = std::make_shared<DimToValMap>(); - mCurrentBucketStartTimeNs = nextBucketStartTimeNs; -} - -// Rough estimate of CountMetricProducer buffer stored. This number will be -// greater than actual data size as it contains each dimension of -// CountMetricData is duplicated. -size_t CountMetricProducer::byteSizeLocked() const { - size_t totalSize = 0; - for (const auto& pair : mPastBuckets) { - totalSize += pair.second.size() * kBucketSize; - } - return totalSize; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h deleted file mode 100644 index 0769ac459b8c..000000000000 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ /dev/null @@ -1,143 +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. - */ - -#ifndef COUNT_METRIC_PRODUCER_H -#define COUNT_METRIC_PRODUCER_H - -#include <android/util/ProtoOutputStream.h> -#include <gtest/gtest_prod.h> - -#include <unordered_map> - -#include "MetricProducer.h" -#include "anomaly/AnomalyTracker.h" -#include "condition/ConditionTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/matcher_util.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -struct CountBucket { - int64_t mBucketStartNs; - int64_t mBucketEndNs; - int64_t mCount; -}; - -class CountMetricProducer : public MetricProducer { -public: - CountMetricProducer( - const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs, - const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, - const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap = {}, - const vector<int>& slicedStateAtoms = {}, - const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {}); - - virtual ~CountMetricProducer(); - - void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const FieldValue& oldState, - const FieldValue& newState) override; - - MetricType getMetricType() const override { - return METRIC_TYPE_COUNT; - } - -protected: - void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const std::map<int, HashableDimensionKey>& statePrimaryKeys) override; - -private: - - void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - android::util::ProtoOutputStream* protoOutput) override; - - void clearPastBucketsLocked(const int64_t dumpTimeNs) override; - - // Internal interface to handle condition change. - void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override; - - // Internal interface to handle sliced condition change. - void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - - // Internal function to calculate the current used bytes. - size_t byteSizeLocked() const override; - - void dumpStatesLocked(FILE* out, bool verbose) const override; - - void dropDataLocked(const int64_t dropTimeNs) override; - - // Util function to flush the old packet. - void flushIfNeededLocked(const int64_t& newEventTime) override; - - void flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) override; - - bool onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const sp<ConditionWizard>& wizard, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation) override; - - std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets; - - // The current bucket (may be a partial bucket). - std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>(); - - // The sum of previous partial buckets in the current full bucket (excluding the current - // partial bucket). This is only updated while flushing the current bucket. - std::shared_ptr<DimToValMap> mCurrentFullCounters = std::make_shared<DimToValMap>(); - - static const size_t kBucketSize = sizeof(CountBucket{}); - - bool hitGuardRailLocked(const MetricDimensionKey& newKey); - - FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents); - FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition); - FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition); - FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); - FRIEND_TEST(CountMetricProducerTest, TestFirstBucket); - FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit); - - FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket); - FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket); -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // COUNT_METRIC_PRODUCER_H diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp deleted file mode 100644 index 8869241ab8aa..000000000000 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ /dev/null @@ -1,747 +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. - */ - -#define DEBUG false - -#include "Log.h" - -#include "DurationMetricProducer.h" - -#include <limits.h> -#include <stdlib.h> - -#include "guardrail/StatsdStats.h" -#include "metrics/parsing_utils/metrics_manager_util.h" -#include "stats_log_util.h" -#include "stats_util.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::string; -using std::unordered_map; -using std::vector; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -// for StatsLogReport -const int FIELD_ID_ID = 1; -const int FIELD_ID_DURATION_METRICS = 6; -const int FIELD_ID_TIME_BASE = 9; -const int FIELD_ID_BUCKET_SIZE = 10; -const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; -const int FIELD_ID_IS_ACTIVE = 14; -// for DurationMetricDataWrapper -const int FIELD_ID_DATA = 1; -// for DurationMetricData -const int FIELD_ID_DIMENSION_IN_WHAT = 1; -const int FIELD_ID_BUCKET_INFO = 3; -const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4; -const int FIELD_ID_SLICE_BY_STATE = 6; -// for DurationBucketInfo -const int FIELD_ID_DURATION = 3; -const int FIELD_ID_BUCKET_NUM = 4; -const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5; -const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; - -DurationMetricProducer::DurationMetricProducer( - const ConfigKey& key, const DurationMetric& metric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const int startIndex, - const int stopIndex, const int stopAllIndex, const bool nesting, - const sp<ConditionWizard>& wizard, const uint64_t protoHash, - const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, - const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, - const vector<int>& slicedStateAtoms, - const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, - protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms, - stateGroupMap), - mAggregationType(metric.aggregation_type()), - mStartIndex(startIndex), - mStopIndex(stopIndex), - mStopAllIndex(stopAllIndex), - mNested(nesting), - mContainANYPositionInInternalDimensions(false) { - if (metric.has_bucket()) { - mBucketSizeNs = - TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; - } else { - mBucketSizeNs = LLONG_MAX; - } - - if (metric.has_dimensions_in_what()) { - translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); - mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); - } - - if (internalDimensions.has_field()) { - translateFieldMatcher(internalDimensions, &mInternalDimensions); - mContainANYPositionInInternalDimensions = HasPositionANY(internalDimensions); - } - if (mContainANYPositionInInternalDimensions) { - ALOGE("Position ANY in internal dimension not supported."); - } - if (mContainANYPositionInDimensionsInWhat) { - ALOGE("Position ANY in dimension_in_what not supported."); - } - - mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()); - - if (metric.links().size() > 0) { - for (const auto& link : metric.links()) { - Metric2Condition mc; - mc.conditionId = link.condition(); - translateFieldMatcher(link.fields_in_what(), &mc.metricFields); - translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); - mMetric2ConditionLinks.push_back(mc); - } - mConditionSliced = true; - } - mUnSlicedPartCondition = ConditionState::kUnknown; - - for (const auto& stateLink : metric.state_link()) { - Metric2State ms; - ms.stateAtomId = stateLink.state_atom_id(); - translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields); - translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields); - mMetric2StateLinks.push_back(ms); - } - - mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions); - if (mWizard != nullptr && mConditionTrackerIndex >= 0 && - mMetric2ConditionLinks.size() == 1) { - mHasLinksToAllConditionDimensionsInTracker = mWizard->equalOutputDimensions( - mConditionTrackerIndex, mMetric2ConditionLinks.begin()->conditionFields); - } - flushIfNeededLocked(startTimeNs); - // Adjust start for partial bucket - mCurrentBucketStartTimeNs = startTimeNs; - VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), - (long long)mBucketSizeNs, (long long)mTimeBaseNs); -} - -DurationMetricProducer::~DurationMetricProducer() { - VLOG("~DurationMetric() called"); -} - -bool DurationMetricProducer::onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - if (!MetricProducer::onConfigUpdatedLocked( - config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return false; - } - - const DurationMetric& metric = config.duration_metric(configIndex); - const auto& what_it = conditionTrackerMap.find(metric.what()); - if (what_it == conditionTrackerMap.end()) { - ALOGE("DurationMetric's \"what\" is not present in the config"); - return false; - } - - const Predicate& durationWhat = config.predicate(what_it->second); - if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { - ALOGE("DurationMetric's \"what\" must be a simple condition"); - return false; - } - - const SimplePredicate& simplePredicate = durationWhat.simple_predicate(); - - // Update indices: mStartIndex, mStopIndex, mStopAllIndex, mConditionIndex and MetricsManager - // maps. - if (!handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, mStartIndex)) { - ALOGE("Duration metrics must specify a valid start event matcher"); - return false; - } - - if (simplePredicate.has_stop() && - !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, mStopIndex)) { - return false; - } - - if (simplePredicate.has_stop_all() && - !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, mStopAllIndex)) { - return false; - } - - if (metric.has_condition() && - !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, mConditionTrackerIndex, - conditionToMetricMap)) { - return false; - } - - for (const auto& it : mCurrentSlicedDurationTrackerMap) { - it.second->onConfigUpdated(wizard, mConditionTrackerIndex); - } - - return true; -} - -sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker( - const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) { - std::lock_guard<std::mutex> lock(mMutex); - if (mAggregationType == DurationMetric_AggregationType_SUM) { - if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) { - ALOGW("invalid alert for SUM: threshold (%f) > possible recordable value (%d x %lld)", - alert.trigger_if_sum_gt(), alert.num_buckets(), (long long)mBucketSizeNs); - return nullptr; - } - } - sp<AnomalyTracker> anomalyTracker = - new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor); - addAnomalyTrackerLocked(anomalyTracker); - return anomalyTracker; -} - -// Adds an AnomalyTracker that has already been created. -// Note: this gets called on config updates, and will only get called if the metric and the -// associated alert are preserved, which means the AnomalyTracker must be a DurationAnomalyTracker. -void DurationMetricProducer::addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) { - std::lock_guard<std::mutex> lock(mMutex); - addAnomalyTrackerLocked(anomalyTracker); -} - -void DurationMetricProducer::addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker) { - mAnomalyTrackers.push_back(anomalyTracker); - for (const auto& [_, durationTracker] : mCurrentSlicedDurationTrackerMap) { - durationTracker->addAnomalyTracker(anomalyTracker); - } -} -void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, - const FieldValue& oldState, - const FieldValue& newState) { - // Check if this metric has a StateMap. If so, map the new state value to - // the correct state group id. - FieldValue newStateCopy = newState; - mapStateValue(atomId, &newStateCopy); - - flushIfNeededLocked(eventTimeNs); - - // Each duration tracker is mapped to a different whatKey (a set of values from the - // dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the - // state change event are a subset of the tracker's whatKey field values. - // - // Ex. For a duration metric dimensioned on uid and tag: - // DurationTracker1 whatKey = uid: 1001, tag: 1 - // DurationTracker2 whatKey = uid: 1002, tag 1 - // - // If the state change primaryKey = uid: 1001, we only notify DurationTracker1 of a state - // change. - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) { - continue; - } - whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy); - } -} - -unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( - const MetricDimensionKey& eventKey) const { - switch (mAggregationType) { - case DurationMetric_AggregationType_SUM: - return make_unique<OringDurationTracker>( - mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested, - mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs, - mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers); - case DurationMetric_AggregationType_MAX_SPARSE: - return make_unique<MaxDurationTracker>( - mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested, - mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs, - mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers); - } -} - -// SlicedConditionChange optimization case 1: -// 1. If combination condition, logical operation is AND, only one sliced child predicate. -// 2. The links covers all dimension fields in the sliced child condition predicate. -void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool condition, - const int64_t eventTime) { - if (mMetric2ConditionLinks.size() != 1 || - !mHasLinksToAllConditionDimensionsInTracker) { - return; - } - - bool currentUnSlicedPartCondition = true; - if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) { - ConditionState unslicedPartState = - mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex); - // When the unsliced part is still false, return directly. - if (mUnSlicedPartCondition == ConditionState::kFalse && - unslicedPartState == ConditionState::kFalse) { - return; - } - mUnSlicedPartCondition = unslicedPartState; - currentUnSlicedPartCondition = mUnSlicedPartCondition > 0; - } - - auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex); - auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex); - - // The condition change is from the unsliced predicates. - // We need to find out the true dimensions from the sliced predicate and flip their condition - // state based on the new unsliced condition state. - if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr || - (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) { - std::set<HashableDimensionKey> trueConditionDimensions; - mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions); - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - HashableDimensionKey linkedConditionDimensionKey; - getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0], - &linkedConditionDimensionKey); - if (trueConditionDimensions.find(linkedConditionDimensionKey) != - trueConditionDimensions.end()) { - whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime); - } - } - } else { - // Handle the condition change from the sliced predicate. - if (currentUnSlicedPartCondition) { - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - HashableDimensionKey linkedConditionDimensionKey; - getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0], - &linkedConditionDimensionKey); - if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) != - dimensionsChangedToTrue->end()) { - whatIt.second->onConditionChanged(true, eventTime); - } - if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) != - dimensionsChangedToFalse->end()) { - whatIt.second->onConditionChanged(false, eventTime); - } - } - } - } -} - -void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(bool overallCondition, - const int64_t eventTimeNs) { - bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex); - if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker) { - onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTimeNs); - return; - } - - // Now for each of the on-going event, check if the condition has changed for them. - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->onSlicedConditionMayChange(overallCondition, eventTimeNs); - } -} - -void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) { - VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); - - if (!mIsActive) { - return; - } - - flushIfNeededLocked(eventTime); - - if (!mConditionSliced) { - return; - } - - onSlicedConditionMayChangeInternalLocked(overallCondition, eventTime); -} - -void DurationMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) { - MetricProducer::onActiveStateChangedLocked(eventTimeNs); - - if (!mConditionSliced) { - if (ConditionState::kTrue != mCondition) { - return; - } - - if (mIsActive) { - flushIfNeededLocked(eventTimeNs); - } - - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->onConditionChanged(mIsActive, eventTimeNs); - } - } else if (mIsActive) { - flushIfNeededLocked(eventTimeNs); - onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs); - } else { // mConditionSliced == true && !mIsActive - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->onConditionChanged(mIsActive, eventTimeNs); - } - } -} - -void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, - const int64_t eventTime) { - VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; - - if (!mIsActive) { - return; - } - - flushIfNeededLocked(eventTime); - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->onConditionChanged(conditionMet, eventTime); - } -} - -void DurationMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - flushIfNeededLocked(dropTimeNs); - StatsdStats::getInstance().noteBucketDropped(mMetricId); - mPastBuckets.clear(); -} - -void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - flushIfNeededLocked(dumpTimeNs); - mPastBuckets.clear(); -} - -void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - ProtoOutputStream* protoOutput) { - if (include_current_partial_bucket) { - flushLocked(dumpTimeNs); - } else { - flushIfNeededLocked(dumpTimeNs); - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); - - if (mPastBuckets.empty()) { - VLOG(" Duration metric, empty return"); - return; - } - - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs); - - if (!mSliceByPositionALL) { - if (!mDimensionsInWhat.empty()) { - uint64_t dimenPathToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT); - writeDimensionPathToProto(mDimensionsInWhat, protoOutput); - protoOutput->end(dimenPathToken); - } - } - - uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS); - - VLOG("Duration metric %lld dump report now...", (long long)mMetricId); - - for (const auto& pair : mPastBuckets) { - const MetricDimensionKey& dimensionKey = pair.first; - VLOG(" dimension key %s", dimensionKey.toString().c_str()); - - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - - // First fill dimension. - if (mSliceByPositionALL) { - uint64_t dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput); - protoOutput->end(dimensionToken); - } else { - writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(), - FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput); - } - // Then fill slice_by_state. - for (auto state : dimensionKey.getStateValuesKey().getValues()) { - uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SLICE_BY_STATE); - writeStateToProto(state, protoOutput); - protoOutput->end(stateToken); - } - // Then fill bucket_info (DurationBucketInfo). - for (const auto& bucket : pair.second) { - uint64_t bucketInfoToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketStartNs)); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketEndNs)); - } else { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM, - (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs))); - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration); - protoOutput->end(bucketInfoToken); - VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs, - (long long)bucket.mBucketEndNs, (long long)bucket.mDuration); - } - - protoOutput->end(wrapperToken); - } - - protoOutput->end(protoToken); - if (erase_data) { - mPastBuckets.clear(); - } -} - -void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { - int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - - if (currentBucketEndTimeNs > eventTimeNs) { - return; - } - VLOG("flushing..........."); - int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; - int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; - flushCurrentBucketLocked(eventTimeNs, nextBucketNs); - - mCurrentBucketNum += numBucketsForward; -} - -void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) { - for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin(); - whatIt != mCurrentSlicedDurationTrackerMap.end();) { - if (whatIt->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) { - VLOG("erase bucket for key %s", whatIt->first.toString().c_str()); - whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt); - } else { - ++whatIt; - } - } - StatsdStats::getInstance().noteBucketCount(mMetricId); - mCurrentBucketStartTimeNs = nextBucketStartTimeNs; -} - -void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { - if (mCurrentSlicedDurationTrackerMap.size() == 0) { - return; - } - - fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId, - (unsigned long)mCurrentSlicedDurationTrackerMap.size()); - if (verbose) { - for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) { - fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str()); - whatIt.second->dumpStates(out, verbose); - } - } -} - -bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { - auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat()); - if (whatIt == mCurrentSlicedDurationTrackerMap.end()) { - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize( - mConfigKey, mMetricId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("DurationMetric %lld dropping data for what dimension key %s", - (long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str()); - StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); - return true; - } - } - } - return false; -} - -void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, - bool condition, const LogEvent& event) { - const auto& whatKey = eventKey.getDimensionKeyInWhat(); - auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey); - if (whatIt == mCurrentSlicedDurationTrackerMap.end()) { - if (hitGuardRailLocked(eventKey)) { - return; - } - mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey); - } - - auto it = mCurrentSlicedDurationTrackerMap.find(whatKey); - if (mUseWhatDimensionAsInternalDimension) { - it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys); - return; - } - - if (mInternalDimensions.empty()) { - it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(), - conditionKeys); - } else { - HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY; - filterValues(mInternalDimensions, event.getValues(), &dimensionKey); - it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(), - conditionKeys); - } - -} - -void DurationMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, bool condition, const LogEvent& event, - const map<int, HashableDimensionKey>& statePrimaryKeys) { - ALOGW("Not used in duration tracker."); -} - -void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, - const LogEvent& event) { - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - if (eventTimeNs < mTimeBaseNs) { - return; - } - - if (mIsActive) { - flushIfNeededLocked(event.GetElapsedTimestampNs()); - } - - // Handles Stopall events. - if ((int)matcherIndex == mStopAllIndex) { - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->noteStopAll(event.GetElapsedTimestampNs()); - } - return; - } - - HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY; - if (!mDimensionsInWhat.empty()) { - filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); - } - - // Stores atom id to primary key pairs for each state atom that the metric is - // sliced by. - std::map<int, HashableDimensionKey> statePrimaryKeys; - - // For states with primary fields, use MetricStateLinks to get the primary - // field values from the log event. These values will form a primary key - // that will be used to query StateTracker for the correct state value. - for (const auto& stateLink : mMetric2StateLinks) { - getDimensionForState(event.getValues(), stateLink, - &statePrimaryKeys[stateLink.stateAtomId]); - } - - // For each sliced state, query StateTracker for the state value using - // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY. - // - // Expected functionality: for any case where the MetricStateLinks are - // initialized incorrectly (ex. # of state links != # of primary fields, no - // links are provided for a state with primary fields, links are provided - // in the wrong order, etc.), StateTracker will simply return kStateUnknown - // when queried using an incorrect key. - HashableDimensionKey stateValuesKey = DEFAULT_DIMENSION_KEY; - for (auto atomId : mSlicedStateAtoms) { - FieldValue value; - if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) { - // found a primary key for this state, query using the key - queryStateValue(atomId, statePrimaryKeys[atomId], &value); - } else { - // if no MetricStateLinks exist for this state atom, - // query using the default dimension key (empty HashableDimensionKey) - queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value); - } - mapStateValue(atomId, &value); - stateValuesKey.addValue(value); - } - - // Handles Stop events. - if ((int)matcherIndex == mStopIndex) { - if (mUseWhatDimensionAsInternalDimension) { - auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); - if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false); - } - return; - } - - HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY; - if (!mInternalDimensions.empty()) { - filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey); - } - - auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); - if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false); - } - return; - } - - bool condition; - ConditionKey conditionKey; - if (mConditionSliced) { - for (const auto& link : mMetric2ConditionLinks) { - getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]); - } - - auto conditionState = - mWizard->query(mConditionTrackerIndex, conditionKey, - !mHasLinksToAllConditionDimensionsInTracker); - condition = conditionState == ConditionState::kTrue; - } else { - // TODO: The unknown condition state is not handled here, we should fix it. - condition = mCondition == ConditionState::kTrue; - } - - condition = condition && mIsActive; - - handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition, - event); -} - -size_t DurationMetricProducer::byteSizeLocked() const { - size_t totalSize = 0; - for (const auto& pair : mPastBuckets) { - totalSize += pair.second.size() * kBucketSize; - } - return totalSize; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h deleted file mode 100644 index 5feb09fc1c98..000000000000 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ /dev/null @@ -1,194 +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. - */ - -#pragma once - - -#include <unordered_map> - -#include <android/util/ProtoOutputStream.h> -#include "../anomaly/DurationAnomalyTracker.h" -#include "../condition/ConditionTracker.h" -#include "../matchers/matcher_util.h" -#include "MetricProducer.h" -#include "duration_helper/DurationTracker.h" -#include "duration_helper/MaxDurationTracker.h" -#include "duration_helper/OringDurationTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "stats_util.h" - -using namespace std; - -namespace android { -namespace os { -namespace statsd { - -class DurationMetricProducer : public MetricProducer { -public: - DurationMetricProducer( - const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const int startIndex, - const int stopIndex, const int stopAllIndex, const bool nesting, - const sp<ConditionWizard>& wizard, const uint64_t protoHash, - const FieldMatcher& internalDimensions, const int64_t timeBaseNs, - const int64_t startTimeNs, - const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {}, - const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {}, - const vector<int>& slicedStateAtoms = {}, - const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {}); - - virtual ~DurationMetricProducer(); - - sp<AnomalyTracker> addAnomalyTracker(const Alert &alert, - const sp<AlarmMonitor>& anomalyAlarmMonitor) override; - - void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) override; - - void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const FieldValue& oldState, - const FieldValue& newState) override; - - MetricType getMetricType() const override { - return METRIC_TYPE_DURATION; - } - -protected: - void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override; - - void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, bool condition, const LogEvent& event, - const std::map<int, HashableDimensionKey>& statePrimaryKeys) override; - -private: - void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, - bool condition, const LogEvent& event); - - void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - android::util::ProtoOutputStream* protoOutput) override; - - void clearPastBucketsLocked(const int64_t dumpTimeNs) override; - - // Internal interface to handle condition change. - void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override; - - // Internal interface to handle active state change. - void onActiveStateChangedLocked(const int64_t& eventTimeNs) override; - - // Internal interface to handle sliced condition change. - void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - - void onSlicedConditionMayChangeInternalLocked(bool overallCondition, - const int64_t eventTimeNs); - - void onSlicedConditionMayChangeLocked_opt1(bool overallCondition, const int64_t eventTime); - void onSlicedConditionMayChangeLocked_opt2(bool overallCondition, const int64_t eventTime); - - // Internal function to calculate the current used bytes. - size_t byteSizeLocked() const override; - - void dumpStatesLocked(FILE* out, bool verbose) const override; - - void dropDataLocked(const int64_t dropTimeNs) override; - - // Util function to flush the old packet. - void flushIfNeededLocked(const int64_t& eventTime); - - void flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) override; - - bool onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const sp<ConditionWizard>& wizard, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation) override; - - void addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker); - - const DurationMetric_AggregationType mAggregationType; - - // Index of the SimpleAtomMatcher which defines the start. - int mStartIndex; - - // Index of the SimpleAtomMatcher which defines the stop. - int mStopIndex; - - // Index of the SimpleAtomMatcher which defines the stop all for all dimensions. - int mStopAllIndex; - - // nest counting -- for the same key, stops must match the number of starts to make real stop - const bool mNested; - - // The dimension from the atom predicate. e.g., uid, wakelock name. - vector<Matcher> mInternalDimensions; - - bool mContainANYPositionInInternalDimensions; - - // This boolean is true iff When mInternalDimensions == mDimensionsInWhat - bool mUseWhatDimensionAsInternalDimension; - - // Caches the current unsliced part condition. - ConditionState mUnSlicedPartCondition; - - // Save the past buckets and we can clear when the StatsLogReport is dumped. - std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets; - - // The duration trackers in the current bucket. - std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>> - mCurrentSlicedDurationTrackerMap; - - // Helper function to create a duration tracker given the metric aggregation type. - std::unique_ptr<DurationTracker> createDurationTracker( - const MetricDimensionKey& eventKey) const; - - // Util function to check whether the specified dimension hits the guardrail. - bool hitGuardRailLocked(const MetricDimensionKey& newKey); - - static const size_t kBucketSize = sizeof(DurationBucket{}); - - FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition); - FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition); - FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates); - FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket); - - FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestSumDuration); - FRIEND_TEST(DurationMetricProducerTest_PartialBucket, - TestSumDurationWithSplitInFollowingBucket); - FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration); - FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket); - - FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics); - FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp deleted file mode 100644 index ca302c0e71fb..000000000000 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ /dev/null @@ -1,214 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "EventMetricProducer.h" - -#include <limits.h> -#include <stdlib.h> - -#include "metrics/parsing_utils/metrics_manager_util.h" -#include "stats_log_util.h" -#include "stats_util.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_STRING; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::ProtoOutputStream; -using std::map; -using std::string; -using std::unordered_map; -using std::vector; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -// for StatsLogReport -const int FIELD_ID_ID = 1; -const int FIELD_ID_EVENT_METRICS = 4; -const int FIELD_ID_IS_ACTIVE = 14; -// for EventMetricDataWrapper -const int FIELD_ID_DATA = 1; -// for EventMetricData -const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1; -const int FIELD_ID_ATOMS = 2; - -EventMetricProducer::EventMetricProducer( - const ConfigKey& key, const EventMetric& metric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const uint64_t protoHash, const int64_t startTimeNs, - const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, - const vector<int>& slicedStateAtoms, - const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) - : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, initialConditionCache, wizard, - protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms, - stateGroupMap) { - if (metric.links().size() > 0) { - for (const auto& link : metric.links()) { - Metric2Condition mc; - mc.conditionId = link.condition(); - translateFieldMatcher(link.fields_in_what(), &mc.metricFields); - translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); - mMetric2ConditionLinks.push_back(mc); - } - mConditionSliced = true; - } - mProto = std::make_unique<ProtoOutputStream>(); - VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), - (long long)mBucketSizeNs, (long long)mTimeBaseNs); -} - -EventMetricProducer::~EventMetricProducer() { - VLOG("~EventMetricProducer() called"); -} - -bool EventMetricProducer::onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - if (!MetricProducer::onConfigUpdatedLocked( - config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return false; - } - - const EventMetric& metric = config.event_metric(configIndex); - int trackerIndex; - // Update appropriate indices, specifically mConditionIndex and MetricsManager maps. - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { - return false; - } - - if (metric.has_condition() && - !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, mConditionTrackerIndex, - conditionToMetricMap)) { - return false; - } - return true; -} - -void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - mProto->clear(); - StatsdStats::getInstance().noteBucketDropped(mMetricId); -} - -void EventMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) { -} - -std::unique_ptr<std::vector<uint8_t>> serializeProtoLocked(ProtoOutputStream& protoOutput) { - size_t bufferSize = protoOutput.size(); - - std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize)); - - size_t pos = 0; - sp<android::util::ProtoReader> reader = protoOutput.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((*buffer)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - return buffer; -} - -void EventMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - mProto->clear(); -} - -void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - ProtoOutputStream* protoOutput) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); - if (mProto->size() <= 0) { - return; - } - - size_t bufferSize = mProto->size(); - VLOG("metric %lld dump report now... proto size: %zu ", - (long long)mMetricId, bufferSize); - std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked(*mProto); - - protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS, - reinterpret_cast<char*>(buffer.get()->data()), buffer.get()->size()); - - if (erase_data) { - mProto->clear(); - } -} - -void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, - const int64_t eventTime) { - VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; -} - -void EventMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const map<int, HashableDimensionKey>& statePrimaryKeys) { - if (!condition) { - return; - } - - uint64_t wrapperToken = - mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - const int64_t elapsedTimeNs = truncateTimestampIfNecessary(event); - mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long) elapsedTimeNs); - - uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS); - event.ToProto(*mProto); - mProto->end(eventToken); - mProto->end(wrapperToken); -} - -size_t EventMetricProducer::byteSizeLocked() const { - return mProto->bytesWritten(); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h deleted file mode 100644 index 3347d7b6aab5..000000000000 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ /dev/null @@ -1,103 +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. - */ - -#ifndef EVENT_METRIC_PRODUCER_H -#define EVENT_METRIC_PRODUCER_H - -#include <unordered_map> - -#include <android/util/ProtoOutputStream.h> - -#include "../condition/ConditionTracker.h" -#include "../matchers/matcher_util.h" -#include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -class EventMetricProducer : public MetricProducer { -public: - EventMetricProducer( - const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const uint64_t protoHash, const int64_t startTimeNs, - const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, - const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap = {}, - const vector<int>& slicedStateAtoms = {}, - const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {}); - - virtual ~EventMetricProducer(); - - MetricType getMetricType() const override { - return METRIC_TYPE_EVENT; - } - -private: - void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const std::map<int, HashableDimensionKey>& statePrimaryKeys) override; - - void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - android::util::ProtoOutputStream* protoOutput) override; - void clearPastBucketsLocked(const int64_t dumpTimeNs) override; - - // Internal interface to handle condition change. - void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override; - - // Internal interface to handle sliced condition change. - void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - - bool onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const sp<ConditionWizard>& wizard, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation) override; - - void dropDataLocked(const int64_t dropTimeNs) override; - - // Internal function to calculate the current used bytes. - size_t byteSizeLocked() const override; - - void dumpStatesLocked(FILE* out, bool verbose) const override{}; - - // Maps to a EventMetricDataWrapper. Storing atom events in ProtoOutputStream - // is more space efficient than storing LogEvent. - std::unique_ptr<android::util::ProtoOutputStream> mProto; -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // EVENT_METRIC_PRODUCER_H diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp deleted file mode 100644 index 2a37b587fbce..000000000000 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ /dev/null @@ -1,675 +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. -*/ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "GaugeMetricProducer.h" - -#include "guardrail/StatsdStats.h" -#include "metrics/parsing_utils/metrics_manager_util.h" -#include "stats_log_util.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::map; -using std::string; -using std::unordered_map; -using std::vector; -using std::make_shared; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -// for StatsLogReport -const int FIELD_ID_ID = 1; -const int FIELD_ID_GAUGE_METRICS = 8; -const int FIELD_ID_TIME_BASE = 9; -const int FIELD_ID_BUCKET_SIZE = 10; -const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; -const int FIELD_ID_IS_ACTIVE = 14; -// for GaugeMetricDataWrapper -const int FIELD_ID_DATA = 1; -const int FIELD_ID_SKIPPED = 2; -// for SkippedBuckets -const int FIELD_ID_SKIPPED_START_MILLIS = 3; -const int FIELD_ID_SKIPPED_END_MILLIS = 4; -const int FIELD_ID_SKIPPED_DROP_EVENT = 5; -// for DumpEvent Proto -const int FIELD_ID_BUCKET_DROP_REASON = 1; -const int FIELD_ID_DROP_TIME = 2; -// for GaugeMetricData -const int FIELD_ID_DIMENSION_IN_WHAT = 1; -const int FIELD_ID_BUCKET_INFO = 3; -const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4; -// for GaugeBucketInfo -const int FIELD_ID_ATOM = 3; -const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4; -const int FIELD_ID_BUCKET_NUM = 6; -const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 7; -const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8; - -GaugeMetricProducer::GaugeMetricProducer( - const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const uint64_t protoHash, const int whatMatcherIndex, - const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId, - const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs, - const sp<StatsPullerManager>& pullerManager, - const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, - protoHash, eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{}, - /*stateGroupMap=*/{}), - mWhatMatcherIndex(whatMatcherIndex), - mEventMatcherWizard(matcherWizard), - mPullerManager(pullerManager), - mPullTagId(pullTagId), - mTriggerAtomId(triggerAtomId), - mAtomId(atomId), - mIsPulled(pullTagId != -1), - mMinBucketSizeNs(metric.min_bucket_size_nanos()), - mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC - : StatsdStats::kPullMaxDelayNs), - mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) != - StatsdStats::kAtomDimensionKeySizeLimitMap.end() - ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first - : StatsdStats::kDimensionKeySizeSoftLimit), - mDimensionHardLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) != - StatsdStats::kAtomDimensionKeySizeLimitMap.end() - ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second - : StatsdStats::kDimensionKeySizeHardLimit), - mGaugeAtomsPerDimensionLimit(metric.max_num_gauge_atoms_per_bucket()), - mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) { - mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>(); - mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>(); - int64_t bucketSizeMills = 0; - if (metric.has_bucket()) { - bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()); - } else { - bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR); - } - mBucketSizeNs = bucketSizeMills * 1000000; - - mSamplingType = metric.sampling_type(); - if (!metric.gauge_fields_filter().include_all()) { - translateFieldMatcher(metric.gauge_fields_filter().fields(), &mFieldMatchers); - } - - if (metric.has_dimensions_in_what()) { - translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); - mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); - } - - if (metric.links().size() > 0) { - for (const auto& link : metric.links()) { - Metric2Condition mc; - mc.conditionId = link.condition(); - translateFieldMatcher(link.fields_in_what(), &mc.metricFields); - translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); - mMetric2ConditionLinks.push_back(mc); - } - mConditionSliced = true; - } - mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()); - - flushIfNeededLocked(startTimeNs); - // Kicks off the puller immediately. - if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(), - mBucketSizeNs); - } - - // Adjust start for partial first bucket and then pull if needed - mCurrentBucketStartTimeNs = startTimeNs; - - VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d", - (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs, - mConditionSliced); -} - -GaugeMetricProducer::~GaugeMetricProducer() { - VLOG("~GaugeMetricProducer() called"); - if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this); - } -} - -bool GaugeMetricProducer::onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - if (!MetricProducer::onConfigUpdatedLocked( - config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return false; - } - - const GaugeMetric& metric = config.gauge_metric(configIndex); - // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps. - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, mWhatMatcherIndex)) { - return false; - } - - // Need to update maps since the index changed, but mTriggerAtomId will not change. - int triggerTrackerIndex; - if (metric.has_trigger_event() && - !handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex, - /*enforceOneAtom=*/true, allAtomMatchingTrackers, - newAtomMatchingTrackerMap, trackerToMetricMap, - triggerTrackerIndex)) { - return false; - } - - if (metric.has_condition() && - !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, mConditionTrackerIndex, - conditionToMetricMap)) { - return false; - } - sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard; - mEventMatcherWizard = matcherWizard; - return true; -} - -void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { - if (mCurrentSlicedBucket == nullptr || - mCurrentSlicedBucket->size() == 0) { - return; - } - - fprintf(out, "GaugeMetric %lld dimension size %lu\n", (long long)mMetricId, - (unsigned long)mCurrentSlicedBucket->size()); - if (verbose) { - for (const auto& it : *mCurrentSlicedBucket) { - fprintf(out, "\t(what)%s\t(states)%s %d atoms\n", - it.first.getDimensionKeyInWhat().toString().c_str(), - it.first.getStateValuesKey().toString().c_str(), (int)it.second.size()); - } - } -} - -void GaugeMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - flushIfNeededLocked(dumpTimeNs); - mPastBuckets.clear(); - mSkippedBuckets.clear(); -} - -void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - ProtoOutputStream* protoOutput) { - VLOG("Gauge metric %lld report now...", (long long)mMetricId); - if (include_current_partial_bucket) { - flushLocked(dumpTimeNs); - } else { - flushIfNeededLocked(dumpTimeNs); - } - - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); - - if (mPastBuckets.empty() && mSkippedBuckets.empty()) { - return; - } - - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs); - - // Fills the dimension path if not slicing by ALL. - if (!mSliceByPositionALL) { - if (!mDimensionsInWhat.empty()) { - uint64_t dimenPathToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT); - writeDimensionPathToProto(mDimensionsInWhat, protoOutput); - protoOutput->end(dimenPathToken); - } - } - - uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS); - - for (const auto& skippedBucket : mSkippedBuckets) { - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS, - (long long)(NanoToMillis(skippedBucket.bucketStartTimeNs))); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS, - (long long)(NanoToMillis(skippedBucket.bucketEndTimeNs))); - - for (const auto& dropEvent : skippedBucket.dropEvents) { - uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SKIPPED_DROP_EVENT); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME, (long long) (NanoToMillis(dropEvent.dropTimeNs))); - protoOutput->end(dropEventToken); - } - protoOutput->end(wrapperToken); - } - - for (const auto& pair : mPastBuckets) { - const MetricDimensionKey& dimensionKey = pair.first; - - VLOG("Gauge dimension key %s", dimensionKey.toString().c_str()); - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - - // First fill dimension. - if (mSliceByPositionALL) { - uint64_t dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput); - protoOutput->end(dimensionToken); - } else { - writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(), - FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput); - } - - // Then fill bucket_info (GaugeBucketInfo). - for (const auto& bucket : pair.second) { - uint64_t bucketInfoToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - - if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketStartNs)); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketEndNs)); - } else { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM, - (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs))); - } - - if (!bucket.mGaugeAtoms.empty()) { - for (const auto& atom : bucket.mGaugeAtoms) { - uint64_t atomsToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ATOM); - writeFieldValueTreeToStream(mAtomId, *(atom.mFields), protoOutput); - protoOutput->end(atomsToken); - } - for (const auto& atom : bucket.mGaugeAtoms) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | - FIELD_ID_ELAPSED_ATOM_TIMESTAMP, - (long long)atom.mElapsedTimestampNs); - } - } - protoOutput->end(bucketInfoToken); - VLOG("Gauge \t bucket [%lld - %lld] includes %d atoms.", - (long long)bucket.mBucketStartNs, (long long)bucket.mBucketEndNs, - (int)bucket.mGaugeAtoms.size()); - } - protoOutput->end(wrapperToken); - } - protoOutput->end(protoToken); - - - if (erase_data) { - mPastBuckets.clear(); - mSkippedBuckets.clear(); - } -} - -void GaugeMetricProducer::prepareFirstBucketLocked() { - if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - pullAndMatchEventsLocked(mCurrentBucketStartTimeNs); - } -} - -void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { - bool triggerPuller = false; - switch(mSamplingType) { - // When the metric wants to do random sampling and there is already one gauge atom for the - // current bucket, do not do it again. - case GaugeMetric::RANDOM_ONE_SAMPLE: { - triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty(); - break; - } - case GaugeMetric::CONDITION_CHANGE_TO_TRUE: { - triggerPuller = mCondition == ConditionState::kTrue; - break; - } - case GaugeMetric::FIRST_N_SAMPLES: { - triggerPuller = mCondition == ConditionState::kTrue; - break; - } - default: - break; - } - if (!triggerPuller) { - return; - } - vector<std::shared_ptr<LogEvent>> allData; - if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) { - ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs); - return; - } - const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs; - StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs); - if (pullDelayNs > mMaxPullDelayNs) { - ALOGE("Pull finish too late for atom %d", mPullTagId); - StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId); - return; - } - for (const auto& data : allData) { - LogEvent localCopy = data->makeCopy(); - localCopy.setElapsedTimestampNs(timestampNs); - if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) == - MatchingState::kMatched) { - onMatchedLogEventLocked(mWhatMatcherIndex, localCopy); - } - } -} - -void GaugeMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) { - MetricProducer::onActiveStateChangedLocked(eventTimeNs); - if (ConditionState::kTrue != mCondition || !mIsPulled) { - return; - } - if (mTriggerAtomId == -1 || (mIsActive && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE)) { - pullAndMatchEventsLocked(eventTimeNs); - } - -} - -void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, - const int64_t eventTimeNs) { - VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId); - - mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; - if (!mIsActive) { - return; - } - - flushIfNeededLocked(eventTimeNs); - if (mIsPulled && mTriggerAtomId == -1) { - pullAndMatchEventsLocked(eventTimeNs); - } // else: Push mode. No need to proactively pull the gauge data. -} - -void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTimeNs) { - VLOG("GaugeMetric %lld onSlicedConditionMayChange overall condition %d", (long long)mMetricId, - overallCondition); - mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse; - if (!mIsActive) { - return; - } - - flushIfNeededLocked(eventTimeNs); - // If the condition is sliced, mCondition is true if any of the dimensions is true. And we will - // pull for every dimension. - if (mIsPulled && mTriggerAtomId == -1) { - pullAndMatchEventsLocked(eventTimeNs); - } // else: Push mode. No need to proactively pull the gauge data. -} - -std::shared_ptr<vector<FieldValue>> GaugeMetricProducer::getGaugeFields(const LogEvent& event) { - std::shared_ptr<vector<FieldValue>> gaugeFields; - if (mFieldMatchers.size() > 0) { - gaugeFields = std::make_shared<vector<FieldValue>>(); - filterGaugeValues(mFieldMatchers, event.getValues(), gaugeFields.get()); - } else { - gaugeFields = std::make_shared<vector<FieldValue>>(event.getValues()); - } - // Trim all dimension fields from output. Dimensions will appear in output report and will - // benefit from dictionary encoding. For large pulled atoms, this can give the benefit of - // optional repeated field. - for (const auto& field : mDimensionsInWhat) { - for (auto it = gaugeFields->begin(); it != gaugeFields->end();) { - if (it->mField.matches(field)) { - it = gaugeFields->erase(it); - } else { - it++; - } - } - } - return gaugeFields; -} - -void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, - bool pullSuccess, int64_t originalPullTimeNs) { - std::lock_guard<std::mutex> lock(mMutex); - if (!pullSuccess || allData.size() == 0) { - return; - } - const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs; - StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs); - if (pullDelayNs > mMaxPullDelayNs) { - ALOGE("Pull finish too late for atom %d", mPullTagId); - StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId); - return; - } - for (const auto& data : allData) { - if (mEventMatcherWizard->matchLogEvent( - *data, mWhatMatcherIndex) == MatchingState::kMatched) { - onMatchedLogEventLocked(mWhatMatcherIndex, *data); - } - } -} - -bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { - if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) { - return false; - } - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentSlicedBucket->size() > mDimensionSoftLimit - 1) { - size_t newTupleCount = mCurrentSlicedBucket->size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > mDimensionHardLimit) { - ALOGE("GaugeMetric %lld dropping data for dimension key %s", - (long long)mMetricId, newKey.toString().c_str()); - StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); - return true; - } - } - - return false; -} - -void GaugeMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const map<int, HashableDimensionKey>& statePrimaryKeys) { - if (condition == false) { - return; - } - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - if (eventTimeNs < mCurrentBucketStartTimeNs) { - VLOG("Gauge Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, - (long long)mCurrentBucketStartTimeNs); - return; - } - flushIfNeededLocked(eventTimeNs); - - if (mTriggerAtomId == event.GetTagId()) { - pullAndMatchEventsLocked(eventTimeNs); - return; - } - - // When gauge metric wants to randomly sample the output atom, we just simply use the first - // gauge in the given bucket. - if (mCurrentSlicedBucket->find(eventKey) != mCurrentSlicedBucket->end() && - mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - return; - } - if (hitGuardRailLocked(eventKey)) { - return; - } - if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) { - return; - } - - const int64_t truncatedElapsedTimestampNs = truncateTimestampIfNecessary(event); - GaugeAtom gaugeAtom(getGaugeFields(event), truncatedElapsedTimestampNs); - (*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom); - // Anomaly detection on gauge metric only works when there is one numeric - // field specified. - if (mAnomalyTrackers.size() > 0) { - if (gaugeAtom.mFields->size() == 1) { - const Value& value = gaugeAtom.mFields->begin()->mValue; - long gaugeVal = 0; - if (value.getType() == INT) { - gaugeVal = (long)value.int_value; - } else if (value.getType() == LONG) { - gaugeVal = value.long_value; - } - for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, - eventKey, gaugeVal); - } - } - } -} - -void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() { - for (const auto& slice : *mCurrentSlicedBucket) { - if (slice.second.empty()) { - continue; - } - const Value& value = slice.second.front().mFields->front().mValue; - long gaugeVal = 0; - if (value.getType() == INT) { - gaugeVal = (long)value.int_value; - } else if (value.getType() == LONG) { - gaugeVal = value.long_value; - } - (*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal; - } -} - -void GaugeMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - flushIfNeededLocked(dropTimeNs); - StatsdStats::getInstance().noteBucketDropped(mMetricId); - mPastBuckets.clear(); -} - -// When a new matched event comes in, we check if event falls into the current -// bucket. If not, flush the old counter to past buckets and initialize the new -// bucket. -// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside -// the GaugeMetricProducer while holding the lock. -void GaugeMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { - int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - - if (eventTimeNs < currentBucketEndTimeNs) { - VLOG("Gauge eventTime is %lld, less than next bucket start time %lld", - (long long)eventTimeNs, (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs)); - return; - } - - // Adjusts the bucket start and end times. - int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; - int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; - flushCurrentBucketLocked(eventTimeNs, nextBucketNs); - - mCurrentBucketNum += numBucketsForward; - VLOG("Gauge metric %lld: new bucket start time: %lld", (long long)mMetricId, - (long long)mCurrentBucketStartTimeNs); -} - -void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) { - int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); - int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs; - - GaugeBucket info; - info.mBucketStartNs = mCurrentBucketStartTimeNs; - info.mBucketEndNs = bucketEndTime; - - // Add bucket to mPastBuckets if bucket is large enough. - // Otherwise, drop the bucket data and add bucket metadata to mSkippedBuckets. - bool isBucketLargeEnough = info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs; - if (isBucketLargeEnough) { - for (const auto& slice : *mCurrentSlicedBucket) { - info.mGaugeAtoms = slice.second; - auto& bucketList = mPastBuckets[slice.first]; - bucketList.push_back(info); - VLOG("Gauge gauge metric %lld, dump key value: %s", (long long)mMetricId, - slice.first.toString().c_str()); - } - } else { - mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs; - mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime; - if (!maxDropEventsReached()) { - mCurrentSkippedBucket.dropEvents.emplace_back( - buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL)); - } - mSkippedBuckets.emplace_back(mCurrentSkippedBucket); - } - - // If we have anomaly trackers, we need to update the partial bucket values. - if (mAnomalyTrackers.size() > 0) { - updateCurrentSlicedBucketForAnomaly(); - - if (eventTimeNs > fullBucketEndTimeNs) { - // This is known to be a full bucket, so send this data to the anomaly tracker. - for (auto& tracker : mAnomalyTrackers) { - tracker->addPastBucket(mCurrentSlicedBucketForAnomaly, mCurrentBucketNum); - } - mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>(); - } - } - - StatsdStats::getInstance().noteBucketCount(mMetricId); - mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>(); - mCurrentBucketStartTimeNs = nextBucketStartTimeNs; - mCurrentSkippedBucket.reset(); -} - -size_t GaugeMetricProducer::byteSizeLocked() const { - size_t totalSize = 0; - for (const auto& pair : mPastBuckets) { - for (const auto& bucket : pair.second) { - totalSize += bucket.mGaugeAtoms.size() * sizeof(GaugeAtom); - for (const auto& atom : bucket.mGaugeAtoms) { - if (atom.mFields != nullptr) { - totalSize += atom.mFields->size() * sizeof(FieldValue); - } - } - } - } - return totalSize; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h deleted file mode 100644 index 9bdaac96c9ef..000000000000 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ /dev/null @@ -1,234 +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. - */ - -#pragma once - -#include <unordered_map> - -#include <android/util/ProtoOutputStream.h> -#include <gtest/gtest_prod.h> -#include "../condition/ConditionTracker.h" -#include "../external/PullDataReceiver.h" -#include "../external/StatsPullerManager.h" -#include "../matchers/matcher_util.h" -#include "../matchers/EventMatcherWizard.h" -#include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "../stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -struct GaugeAtom { - GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs) - : mFields(fields), mElapsedTimestampNs(elapsedTimeNs) { - } - std::shared_ptr<vector<FieldValue>> mFields; - int64_t mElapsedTimestampNs; -}; - -struct GaugeBucket { - int64_t mBucketStartNs; - int64_t mBucketEndNs; - std::vector<GaugeAtom> mGaugeAtoms; -}; - -typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>> - DimToGaugeAtomsMap; - -// This gauge metric producer first register the puller to automatically pull the gauge at the -// beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise -// proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric -// producer always reports the gauge at the earliest time of the bucket when the condition is met. -class GaugeMetricProducer : public MetricProducer, public virtual PullDataReceiver { -public: - GaugeMetricProducer( - const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, - const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash, - const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, - const int pullTagId, const int triggerAtomId, const int atomId, - const int64_t timeBaseNs, const int64_t startTimeNs, - const sp<StatsPullerManager>& pullerManager, - const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, - const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap = {}); - - virtual ~GaugeMetricProducer(); - - // Handles when the pulled data arrives. - void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, - bool pullSuccess, int64_t originalPullTimeNs) override; - - // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. - void notifyAppUpgrade(const int64_t& eventTimeNs) override { - std::lock_guard<std::mutex> lock(mMutex); - - if (!mSplitBucketForAppUpgrade) { - return; - } - flushLocked(eventTimeNs); - if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - pullAndMatchEventsLocked(eventTimeNs); - } - }; - - // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. - void onStatsdInitCompleted(const int64_t& eventTimeNs) override { - std::lock_guard<std::mutex> lock(mMutex); - - flushLocked(eventTimeNs); - if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - pullAndMatchEventsLocked(eventTimeNs); - } - }; - - MetricType getMetricType() const override { - return METRIC_TYPE_GAUGE; - } - -protected: - void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const std::map<int, HashableDimensionKey>& statePrimaryKeys) override; - -private: - void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - android::util::ProtoOutputStream* protoOutput) override; - void clearPastBucketsLocked(const int64_t dumpTimeNs) override; - - // Internal interface to handle condition change. - void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override; - - // Internal interface to handle active state change. - void onActiveStateChangedLocked(const int64_t& eventTimeNs) override; - - // Internal interface to handle sliced condition change. - void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - - // Internal function to calculate the current used bytes. - size_t byteSizeLocked() const override; - - void dumpStatesLocked(FILE* out, bool verbose) const override; - - void dropDataLocked(const int64_t dropTimeNs) override; - - // Util function to flush the old packet. - void flushIfNeededLocked(const int64_t& eventTime) override; - - void flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) override; - - void prepareFirstBucketLocked() override; - - void pullAndMatchEventsLocked(const int64_t timestampNs); - - bool onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const sp<ConditionWizard>& wizard, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation) override; - - int mWhatMatcherIndex; - - sp<EventMatcherWizard> mEventMatcherWizard; - - sp<StatsPullerManager> mPullerManager; - // tagId for pulled data. -1 if this is not pulled - const int mPullTagId; - - // tagId for atoms that trigger the pulling, if any - const int mTriggerAtomId; - - // tagId for output atom - const int mAtomId; - - // if this is pulled metric - const bool mIsPulled; - - // Save the past buckets and we can clear when the StatsLogReport is dumped. - std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets; - - // The current partial bucket. - std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket; - - // The current full bucket for anomaly detection. This is updated to the latest value seen for - // this slice (ie, for partial buckets, we use the last partial bucket in this full bucket). - std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly; - - const int64_t mMinBucketSizeNs; - - // Translate Atom based bucket to single numeric value bucket for anomaly and updates the map - // for each slice with the latest value. - void updateCurrentSlicedBucketForAnomaly(); - - // Allowlist of fields to report. Empty means all are reported. - std::vector<Matcher> mFieldMatchers; - - GaugeMetric::SamplingType mSamplingType; - - const int64_t mMaxPullDelayNs; - - // apply an allowlist on the original input - std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event); - - // Util function to check whether the specified dimension hits the guardrail. - bool hitGuardRailLocked(const MetricDimensionKey& newKey); - - static const size_t kBucketSize = sizeof(GaugeBucket{}); - - const size_t mDimensionSoftLimit; - - const size_t mDimensionHardLimit; - - const size_t mGaugeAtomsPerDimensionLimit; - - const bool mSplitBucketForAppUpgrade; - - FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition); - FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition); - FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition); - FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled); - FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection); - FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket); - FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger); - FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput); - - FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents); - FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled); - - FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp deleted file mode 100644 index 5b321a0e3d60..000000000000 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ /dev/null @@ -1,355 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "MetricProducer.h" - -#include "../guardrail/StatsdStats.h" -#include "metrics/parsing_utils/metrics_manager_util.h" -#include "state/StateTracker.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_ENUM; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::ProtoOutputStream; - -namespace android { -namespace os { -namespace statsd { - - -// for ActiveMetric -const int FIELD_ID_ACTIVE_METRIC_ID = 1; -const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2; - -// for ActiveEventActivation -const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1; -const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2; -const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3; - -MetricProducer::MetricProducer( - const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, - const int conditionIndex, const vector<ConditionState>& initialConditionCache, - const sp<ConditionWizard>& wizard, const uint64_t protoHash, - const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap, - const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap, - const vector<int>& slicedStateAtoms, - const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) - : mMetricId(metricId), - mProtoHash(protoHash), - mConfigKey(key), - mTimeBaseNs(timeBaseNs), - mCurrentBucketStartTimeNs(timeBaseNs), - mCurrentBucketNum(0), - mCondition(initialCondition(conditionIndex, initialConditionCache)), - mConditionTrackerIndex(conditionIndex), - mConditionSliced(false), - mWizard(wizard), - mContainANYPositionInDimensionsInWhat(false), - mSliceByPositionALL(false), - mHasLinksToAllConditionDimensionsInTracker(false), - mEventActivationMap(eventActivationMap), - mEventDeactivationMap(eventDeactivationMap), - mIsActive(mEventActivationMap.empty()), - mSlicedStateAtoms(slicedStateAtoms), - mStateGroupMap(stateGroupMap) { -} - -bool MetricProducer::onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - sp<ConditionWizard> tmpWizard = mWizard; - mWizard = wizard; - - unordered_map<int, shared_ptr<Activation>> newEventActivationMap; - unordered_map<int, vector<shared_ptr<Activation>>> newEventDeactivationMap; - if (!handleMetricActivationOnConfigUpdate( - config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap, - newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap, - newEventDeactivationMap)) { - return false; - } - mEventActivationMap = newEventActivationMap; - mEventDeactivationMap = newEventDeactivationMap; - mAnomalyTrackers.clear(); - return true; -} - -void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { - if (!mIsActive) { - return; - } - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - // this is old event, maybe statsd restarted? - if (eventTimeNs < mTimeBaseNs) { - return; - } - - bool condition; - ConditionKey conditionKey; - if (mConditionSliced) { - for (const auto& link : mMetric2ConditionLinks) { - getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]); - } - auto conditionState = - mWizard->query(mConditionTrackerIndex, conditionKey, - !mHasLinksToAllConditionDimensionsInTracker); - condition = (conditionState == ConditionState::kTrue); - } else { - // TODO: The unknown condition state is not handled here, we should fix it. - condition = mCondition == ConditionState::kTrue; - } - - // Stores atom id to primary key pairs for each state atom that the metric is - // sliced by. - std::map<int32_t, HashableDimensionKey> statePrimaryKeys; - - // For states with primary fields, use MetricStateLinks to get the primary - // field values from the log event. These values will form a primary key - // that will be used to query StateTracker for the correct state value. - for (const auto& stateLink : mMetric2StateLinks) { - getDimensionForState(event.getValues(), stateLink, - &statePrimaryKeys[stateLink.stateAtomId]); - } - - // For each sliced state, query StateTracker for the state value using - // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY. - // - // Expected functionality: for any case where the MetricStateLinks are - // initialized incorrectly (ex. # of state links != # of primary fields, no - // links are provided for a state with primary fields, links are provided - // in the wrong order, etc.), StateTracker will simply return kStateUnknown - // when queried using an incorrect key. - HashableDimensionKey stateValuesKey; - for (auto atomId : mSlicedStateAtoms) { - FieldValue value; - if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) { - // found a primary key for this state, query using the key - queryStateValue(atomId, statePrimaryKeys[atomId], &value); - } else { - // if no MetricStateLinks exist for this state atom, - // query using the default dimension key (empty HashableDimensionKey) - queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value); - } - mapStateValue(atomId, &value); - stateValuesKey.addValue(value); - } - - HashableDimensionKey dimensionInWhat; - filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); - MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey); - onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event, - statePrimaryKeys); -} - -bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) { - bool isActive = mEventActivationMap.empty(); - for (auto& it : mEventActivationMap) { - if (it.second->state == ActivationState::kActive && - elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) { - it.second->state = ActivationState::kNotActive; - } - if (it.second->state == ActivationState::kActive) { - isActive = true; - } - } - return isActive; -} - -void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { - std::lock_guard<std::mutex> lock(mMutex); - if (!mIsActive) { - return; - } - mIsActive = evaluateActiveStateLocked(elapsedTimestampNs); - if (!mIsActive) { - onActiveStateChangedLocked(elapsedTimestampNs); - } -} - -void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) { - auto it = mEventActivationMap.find(activationTrackerIndex); - if (it == mEventActivationMap.end()) { - return; - } - auto& activation = it->second; - if (ACTIVATE_ON_BOOT == activation->activationType) { - if (ActivationState::kNotActive == activation->state) { - activation->state = ActivationState::kActiveOnBoot; - } - // If the Activation is already active or set to kActiveOnBoot, do nothing. - return; - } - activation->start_ns = elapsedTimestampNs; - activation->state = ActivationState::kActive; - bool oldActiveState = mIsActive; - mIsActive = true; - if (!oldActiveState) { // Metric went from not active to active. - onActiveStateChangedLocked(elapsedTimestampNs); - } -} - -void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) { - auto it = mEventDeactivationMap.find(deactivationTrackerIndex); - if (it == mEventDeactivationMap.end()) { - return; - } - for (auto activationToCancelIt : it->second) { - activationToCancelIt->state = ActivationState::kNotActive; - } -} - -void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric, - int64_t currentTimeNs) { - if (mEventActivationMap.size() == 0) { - return; - } - for (int i = 0; i < activeMetric.activation_size(); i++) { - const auto& activeEventActivation = activeMetric.activation(i); - auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index()); - if (it == mEventActivationMap.end()) { - ALOGE("Saved event activation not found"); - continue; - } - auto& activation = it->second; - // If the event activation does not have a state, assume it is active. - if (!activeEventActivation.has_state() || - activeEventActivation.state() == ActiveEventActivation::ACTIVE) { - // We don't want to change the ttl for future activations, so we set the start_ns - // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos - activation->start_ns = - currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns; - activation->state = ActivationState::kActive; - mIsActive = true; - } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) { - activation->state = ActivationState::kActiveOnBoot; - } - } -} - -void MetricProducer::writeActiveMetricToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId); - for (auto& it : mEventActivationMap) { - const int atom_matcher_index = it.first; - const std::shared_ptr<Activation>& activation = it.second; - - if (ActivationState::kNotActive == activation->state || - (ActivationState::kActive == activation->state && - activation->start_ns + activation->ttl_ns < currentTimeNs)) { - continue; - } - - const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ACTIVE_METRIC_ACTIVATION); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX, - atom_matcher_index); - if (ActivationState::kActive == activation->state) { - const int64_t remainingTtlNs = - activation->start_ns + activation->ttl_ns - currentTimeNs; - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, - (long long)remainingTtlNs); - proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, - ActiveEventActivation::ACTIVE); - - } else if (ActivationState::kActiveOnBoot == activation->state) { - if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) { - proto->write( - FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, - (long long)activation->ttl_ns); - proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, - ActiveEventActivation::ACTIVE); - } else if (reason == STATSCOMPANION_DIED) { - // We are saving because of system server death, not due to a device shutdown. - // Next time we load, we do not want to activate metrics that activate on boot. - proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, - ActiveEventActivation::ACTIVATE_ON_BOOT); - } - } - proto->end(activationToken); - } -} - -void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey, - FieldValue* value) { - if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) { - value->mValue = Value(StateTracker::kStateUnknown); - value->mField.setTag(atomId); - ALOGW("StateTracker not found for state atom %d", atomId); - return; - } -} - -void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) { - // check if there is a state map for this atom - auto atomIt = mStateGroupMap.find(atomId); - if (atomIt == mStateGroupMap.end()) { - return; - } - auto valueIt = atomIt->second.find(value->mValue.int_value); - if (valueIt == atomIt->second.end()) { - // state map exists, but value was not put in a state group - // so set mValue to kStateUnknown - // TODO(tsaichristine): handle incomplete state maps - value->mValue.setInt(StateTracker::kStateUnknown); - } else { - // set mValue to group_id - value->mValue.setLong(valueIt->second); - } -} - -HashableDimensionKey MetricProducer::getUnknownStateKey() { - HashableDimensionKey stateKey; - for (auto atom : mSlicedStateAtoms) { - FieldValue fieldValue; - fieldValue.mField.setTag(atom); - fieldValue.mValue.setInt(StateTracker::kStateUnknown); - stateKey.addValue(fieldValue); - } - return stateKey; -} - -DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason) { - DropEvent event; - event.reason = reason; - event.dropTimeNs = dropTimeNs; - return event; -} - -bool MetricProducer::maxDropEventsReached() { - return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h deleted file mode 100644 index 0dc8edae8056..000000000000 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ /dev/null @@ -1,582 +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. - */ - -#ifndef METRIC_PRODUCER_H -#define METRIC_PRODUCER_H - -#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> -#include <utils/RefBase.h> - -#include <unordered_map> - -#include "HashableDimensionKey.h" -#include "anomaly/AnomalyTracker.h" -#include "condition/ConditionWizard.h" -#include "config/ConfigKey.h" -#include "matchers/EventMatcherWizard.h" -#include "matchers/matcher_util.h" -#include "packages/PackageInfoListener.h" -#include "state/StateListener.h" -#include "state/StateManager.h" - -namespace android { -namespace os { -namespace statsd { - -// Keep this in sync with DumpReportReason enum in stats_log.proto -enum DumpReportReason { - DEVICE_SHUTDOWN = 1, - CONFIG_UPDATED = 2, - CONFIG_REMOVED = 3, - GET_DATA_CALLED = 4, - ADB_DUMP = 5, - CONFIG_RESET = 6, - STATSCOMPANION_DIED = 7, - TERMINATION_SIGNAL_RECEIVED = 8 -}; - -// If the metric has no activation requirement, it will be active once the metric producer is -// created. -// If the metric needs to be activated by atoms, the metric producer will start -// with kNotActive state, turn to kActive or kActiveOnBoot when the activation event arrives, become -// kNotActive when it reaches the duration limit (timebomb). If the activation event arrives again -// before or after it expires, the event producer will be re-activated and ttl will be reset. -enum ActivationState { - kNotActive = 0, - kActive = 1, - kActiveOnBoot = 2, -}; - -enum DumpLatency { - // In some cases, we only have a short time range to do the dump, e.g. statsd is being killed. - // We might be able to return all the data in this mode. For instance, pull metrics might need - // to be pulled when the current bucket is requested. - FAST = 1, - // In other cases, it is fine for a dump to take more than a few milliseconds, e.g. config - // updates. - NO_TIME_CONSTRAINTS = 2 -}; - -// Keep this in sync with BucketDropReason enum in stats_log.proto -enum BucketDropReason { - // For ValueMetric, a bucket is dropped during a dump report request iff - // current bucket should be included, a pull is needed (pulled metric and - // condition is true), and we are under fast time constraints. - DUMP_REPORT_REQUESTED = 1, - EVENT_IN_WRONG_BUCKET = 2, - CONDITION_UNKNOWN = 3, - PULL_FAILED = 4, - PULL_DELAYED = 5, - DIMENSION_GUARDRAIL_REACHED = 6, - MULTIPLE_BUCKETS_SKIPPED = 7, - // Not an invalid bucket case, but the bucket is dropped. - BUCKET_TOO_SMALL = 8, - // Not an invalid bucket case, but the bucket is skipped. - NO_DATA = 9 -}; - -enum MetricType { - METRIC_TYPE_EVENT = 1, - METRIC_TYPE_COUNT = 2, - METRIC_TYPE_DURATION = 3, - METRIC_TYPE_GAUGE = 4, - METRIC_TYPE_VALUE = 5, -}; -struct Activation { - Activation(const ActivationType& activationType, const int64_t ttlNs) - : ttl_ns(ttlNs), - start_ns(0), - state(ActivationState::kNotActive), - activationType(activationType) {} - - const int64_t ttl_ns; - int64_t start_ns; - ActivationState state; - const ActivationType activationType; -}; - -struct DropEvent { - // Reason for dropping the bucket and/or marking the bucket invalid. - BucketDropReason reason; - // The timestamp of the drop event. - int64_t dropTimeNs; -}; - -struct SkippedBucket { - // Start time of the dropped bucket. - int64_t bucketStartTimeNs; - // End time of the dropped bucket. - int64_t bucketEndTimeNs; - // List of events that invalidated this bucket. - std::vector<DropEvent> dropEvents; - - void reset() { - bucketStartTimeNs = 0; - bucketEndTimeNs = 0; - dropEvents.clear(); - } -}; - -// A MetricProducer is responsible for compute one single metrics, creating stats log report, and -// writing the report to dropbox. MetricProducers should respond to package changes as required in -// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can -// be a no-op. -class MetricProducer : public virtual android::RefBase, public virtual StateListener { -public: - MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, - const int conditionIndex, const vector<ConditionState>& initialConditionCache, - const sp<ConditionWizard>& wizard, const uint64_t protoHash, - const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap, - const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap, - const vector<int>& slicedStateAtoms, - const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap); - - virtual ~MetricProducer(){}; - - ConditionState initialCondition(const int conditionIndex, - const vector<ConditionState>& initialConditionCache) const { - return conditionIndex >= 0 ? initialConditionCache[conditionIndex] : ConditionState::kTrue; - } - - // Update appropriate state on config updates. Primarily, all indices need to be updated. - // This metric and all of its dependencies are guaranteed to be preserved across the update. - // This function also updates several maps used by metricsManager. - // This function clears all anomaly trackers. All anomaly trackers need to be added again. - bool onConfigUpdated( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const sp<ConditionWizard>& wizard, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation) { - std::lock_guard<std::mutex> lock(mMutex); - return onConfigUpdatedLocked(config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, - matcherWizard, allConditionTrackers, conditionTrackerMap, - wizard, metricToActivationMap, trackerToMetricMap, - conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - }; - - /** - * Force a partial bucket split on app upgrade - */ - virtual void notifyAppUpgrade(const int64_t& eventTimeNs) { - std::lock_guard<std::mutex> lock(mMutex); - flushLocked(eventTimeNs); - }; - - void notifyAppRemoved(const int64_t& eventTimeNs) { - // Force buckets to split on removal also. - notifyAppUpgrade(eventTimeNs); - }; - - /** - * Force a partial bucket split on boot complete. - */ - virtual void onStatsdInitCompleted(const int64_t& eventTimeNs) { - std::lock_guard<std::mutex> lock(mMutex); - flushLocked(eventTimeNs); - } - // Consume the parsed stats log entry that already matched the "what" of the metric. - void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) { - std::lock_guard<std::mutex> lock(mMutex); - onMatchedLogEventLocked(matcherIndex, event); - } - - void onConditionChanged(const bool condition, const int64_t eventTime) { - std::lock_guard<std::mutex> lock(mMutex); - onConditionChangedLocked(condition, eventTime); - } - - void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) { - std::lock_guard<std::mutex> lock(mMutex); - onSlicedConditionMayChangeLocked(overallCondition, eventTime); - } - - bool isConditionSliced() const { - std::lock_guard<std::mutex> lock(mMutex); - return mConditionSliced; - }; - - void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const FieldValue& oldState, - const FieldValue& newState){}; - - // Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp. - // This method clears all the past buckets. - void onDumpReport(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - android::util::ProtoOutputStream* protoOutput) { - std::lock_guard<std::mutex> lock(mMutex); - return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, erase_data, - dumpLatency, str_set, protoOutput); - } - - virtual bool onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const sp<ConditionWizard>& wizard, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); - - void clearPastBuckets(const int64_t dumpTimeNs) { - std::lock_guard<std::mutex> lock(mMutex); - return clearPastBucketsLocked(dumpTimeNs); - } - - void prepareFirstBucket() { - std::lock_guard<std::mutex> lock(mMutex); - prepareFirstBucketLocked(); - } - - // Returns the memory in bytes currently used to store this metric's data. Does not change - // state. - size_t byteSize() const { - std::lock_guard<std::mutex> lock(mMutex); - return byteSizeLocked(); - } - - void dumpStates(FILE* out, bool verbose) const { - std::lock_guard<std::mutex> lock(mMutex); - dumpStatesLocked(out, verbose); - } - - // Let MetricProducer drop in-memory data to save memory. - // We still need to keep future data valid and anomaly tracking work, which means we will - // have to flush old data, informing anomaly trackers then safely drop old data. - // We still keep current bucket data for future metrics' validity. - void dropData(const int64_t dropTimeNs) { - std::lock_guard<std::mutex> lock(mMutex); - dropDataLocked(dropTimeNs); - } - - void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) { - std::lock_guard<std::mutex> lock(mMutex); - loadActiveMetricLocked(activeMetric, currentTimeNs); - } - - void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) { - std::lock_guard<std::mutex> lock(mMutex); - activateLocked(activationTrackerIndex, elapsedTimestampNs); - } - - void cancelEventActivation(int deactivationTrackerIndex) { - std::lock_guard<std::mutex> lock(mMutex); - cancelEventActivationLocked(deactivationTrackerIndex); - } - - bool isActive() const { - std::lock_guard<std::mutex> lock(mMutex); - return isActiveLocked(); - } - - void flushIfExpire(int64_t elapsedTimestampNs); - - void writeActiveMetricToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); - - // Start: getters/setters - inline int64_t getMetricId() const { - return mMetricId; - } - - inline uint64_t getProtoHash() const { - return mProtoHash; - } - - virtual MetricType getMetricType() const = 0; - - // For test only. - inline int64_t getCurrentBucketNum() const { - return mCurrentBucketNum; - } - - int64_t getBucketSizeInNs() const { - std::lock_guard<std::mutex> lock(mMutex); - return mBucketSizeNs; - } - - inline const std::vector<int> getSlicedStateAtoms() { - std::lock_guard<std::mutex> lock(mMutex); - return mSlicedStateAtoms; - } - - /* Adds an AnomalyTracker and returns it. */ - virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert, - const sp<AlarmMonitor>& anomalyAlarmMonitor) { - std::lock_guard<std::mutex> lock(mMutex); - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey); - mAnomalyTrackers.push_back(anomalyTracker); - return anomalyTracker; - } - - /* Adds an AnomalyTracker that has already been created */ - virtual void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) { - std::lock_guard<std::mutex> lock(mMutex); - mAnomalyTrackers.push_back(anomalyTracker); - } - // End: getters/setters -protected: - /** - * Flushes the current bucket if the eventTime is after the current bucket's end time. - */ - virtual void flushIfNeededLocked(const int64_t& eventTime){}; - - /** - * For metrics that aggregate (ie, every metric producer except for EventMetricProducer), - * we need to be able to flush the current buckets on demand (ie, end the current bucket and - * start new bucket). If this function is called when eventTimeNs is greater than the current - * bucket's end timestamp, than we flush up to the end of the latest full bucket; otherwise, - * we assume that we want to flush a partial bucket. The bucket start timestamp and bucket - * number are not changed by this function. This method should only be called by - * flushIfNeededLocked or flushLocked or the app upgrade handler; the caller MUST update the - * bucket timestamp and bucket number as needed. - */ - virtual void flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) {}; - - /** - * Flushes all the data including the current partial bucket. - */ - virtual void flushLocked(const int64_t& eventTimeNs) { - flushIfNeededLocked(eventTimeNs); - flushCurrentBucketLocked(eventTimeNs, eventTimeNs); - }; - - /* - * Individual metrics can implement their own business logic here. All pre-processing is done. - * - * [matcherIndex]: the index of the matcher which matched this event. This is interesting to - * DurationMetric, because it has start/stop/stop_all 3 matchers. - * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have - * dimensions, it will be DEFAULT_DIMENSION_KEY - * [conditionKey]: the keys of conditions which should be used to query the condition for this - * target event (from MetricConditionLink). This is passed to individual metrics - * because DurationMetric needs it to be cached. - * [condition]: whether condition is met. If condition is sliced, this is the result coming from - * query with ConditionWizard; If condition is not sliced, this is the - * nonSlicedCondition. - * [event]: the log event, just in case the metric needs its data, e.g., EventMetric. - */ - virtual void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const map<int, HashableDimensionKey>& statePrimaryKeys) = 0; - - // Consume the parsed stats log entry that already matched the "what" of the metric. - virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event); - virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0; - virtual void onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) = 0; - virtual void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - android::util::ProtoOutputStream* protoOutput) = 0; - virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0; - virtual void prepareFirstBucketLocked(){}; - virtual size_t byteSizeLocked() const = 0; - virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0; - virtual void dropDataLocked(const int64_t dropTimeNs) = 0; - void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs); - void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs); - void cancelEventActivationLocked(int deactivationTrackerIndex); - - bool evaluateActiveStateLocked(int64_t elapsedTimestampNs); - - virtual void onActiveStateChangedLocked(const int64_t& eventTimeNs) { - if (!mIsActive) { - flushLocked(eventTimeNs); - } - } - - inline bool isActiveLocked() const { - return mIsActive; - } - - // Convenience to compute the current bucket's end time, which is always aligned with the - // start time of the metric. - int64_t getCurrentBucketEndTimeNs() const { - return mTimeBaseNs + (mCurrentBucketNum + 1) * mBucketSizeNs; - } - - int64_t getBucketNumFromEndTimeNs(const int64_t endNs) { - return (endNs - mTimeBaseNs) / mBucketSizeNs - 1; - } - - // Query StateManager for original state value using the queryKey. - // The field and value are output. - void queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey, - FieldValue* value); - - // If a state map exists for the given atom, replace the original state - // value with the group id mapped to the value. - // If no state map exists, keep the original state value. - void mapStateValue(const int32_t atomId, FieldValue* value); - - // Returns a HashableDimensionKey with unknown state value for each state - // atom. - HashableDimensionKey getUnknownStateKey(); - - DropEvent buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason); - - // Returns true if the number of drop events in the current bucket has - // exceeded the maximum number allowed, which is currently capped at 10. - bool maxDropEventsReached(); - - const int64_t mMetricId; - - // Hash of the Metric's proto bytes from StatsdConfig, including any activations. - // Used to determine if the definition of this metric has changed across a config update. - const uint64_t mProtoHash; - - const ConfigKey mConfigKey; - - // The time when this metric producer was first created. The end time for the current bucket - // can be computed from this based on mCurrentBucketNum. - int64_t mTimeBaseNs; - - // Start time may not be aligned with the start of statsd if there is an app upgrade in the - // middle of a bucket. - int64_t mCurrentBucketStartTimeNs; - - // Used by anomaly detector to track which bucket we are in. This is not sent with the produced - // report. - int64_t mCurrentBucketNum; - - int64_t mBucketSizeNs; - - ConditionState mCondition; - - int mConditionTrackerIndex; - - bool mConditionSliced; - - sp<ConditionWizard> mWizard; - - bool mContainANYPositionInDimensionsInWhat; - - bool mSliceByPositionALL; - - vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config - - // True iff the metric to condition links cover all dimension fields in the condition tracker. - // This field is always false for combinational condition trackers. - bool mHasLinksToAllConditionDimensionsInTracker; - - std::vector<Metric2Condition> mMetric2ConditionLinks; - - std::vector<sp<AnomalyTracker>> mAnomalyTrackers; - - mutable std::mutex mMutex; - - // When the metric producer has multiple activations, these activations are ORed to determine - // whether the metric producer is ready to generate metrics. - std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap; - - // Maps index of atom matcher for deactivation to a list of Activation structs. - std::unordered_map<int, std::vector<std::shared_ptr<Activation>>> mEventDeactivationMap; - - bool mIsActive; - - // The slice_by_state atom ids defined in statsd_config. - const std::vector<int32_t> mSlicedStateAtoms; - - // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>). - const std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap; - - // MetricStateLinks defined in statsd_config that link fields in the state - // atom to fields in the "what" atom. - std::vector<Metric2State> mMetric2StateLinks; - - SkippedBucket mCurrentSkippedBucket; - // Buckets that were invalidated and had their data dropped. - std::vector<SkippedBucket> mSkippedBuckets; - - FRIEND_TEST(CountMetricE2eTest, TestSlicedState); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); - FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields); - FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges); - - FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); - FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); - FRIEND_TEST(DurationMetricE2eTest, TestWithCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped); - FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset); - - FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); - - FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); - FRIEND_TEST(StatsLogProcessorTest, - TestActivationOnBootMultipleActivationsDifferentActivationTypes); - FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); - - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); - FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); - - FRIEND_TEST(MetricsManagerTest, TestInitialConditions); - - FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations); - FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics); - FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics); - FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics); - FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics); - FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes); - FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts); -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // METRIC_PRODUCER_H diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp deleted file mode 100644 index f9b0a1030eee..000000000000 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ /dev/null @@ -1,773 +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. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "MetricsManager.h" - -#include <private/android_filesystem_config.h> - -#include "CountMetricProducer.h" -#include "condition/CombinationConditionTracker.h" -#include "condition/SimpleConditionTracker.h" -#include "guardrail/StatsdStats.h" -#include "matchers/CombinationAtomMatchingTracker.h" -#include "matchers/SimpleAtomMatchingTracker.h" -#include "parsing_utils/config_update_utils.h" -#include "parsing_utils/metrics_manager_util.h" -#include "state/StateManager.h" -#include "stats_log_util.h" -#include "stats_util.h" -#include "statslog_statsd.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; - -using std::set; -using std::string; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -const int FIELD_ID_METRICS = 1; -const int FIELD_ID_ANNOTATIONS = 7; -const int FIELD_ID_ANNOTATIONS_INT64 = 1; -const int FIELD_ID_ANNOTATIONS_INT32 = 2; - -// for ActiveConfig -const int FIELD_ID_ACTIVE_CONFIG_ID = 1; -const int FIELD_ID_ACTIVE_CONFIG_UID = 2; -const int FIELD_ID_ACTIVE_CONFIG_METRIC = 3; - -MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, - const int64_t timeBaseNs, const int64_t currentTimeNs, - const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor) - : mConfigKey(key), - mUidMap(uidMap), - mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1), - mTtlEndNs(-1), - mLastReportTimeNs(currentTimeNs), - mLastReportWallClockNs(getWallClockNs()), - mPullerManager(pullerManager), - mWhitelistedAtomIds(config.whitelisted_atom_ids().begin(), - config.whitelisted_atom_ids().end()), - mShouldPersistHistory(config.persist_locally()) { - // Init the ttl end timestamp. - refreshTtl(timeBaseNs); - - mConfigValid = initStatsdConfig( - key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, - mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap, - mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap, - mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap, - mDeactivationAtomTrackerToMetricMap, mAlertTrackerMap, mMetricIndexesWithActivation, - mStateProtoHashes, mNoReportMetricIds); - - mHashStringsInReport = config.hash_strings_in_metric_report(); - mVersionStringsInReport = config.version_strings_in_metric_report(); - mInstallerInReport = config.installer_in_metric_report(); - - createAllLogSourcesFromConfig(config); - mPullerManager->RegisterPullUidProvider(mConfigKey, this); - - // Store the sub-configs used. - for (const auto& annotation : config.annotation()) { - mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32()); - } - verifyGuardrailsAndUpdateStatsdStats(); - initializeConfigActiveStatus(); -} - -MetricsManager::~MetricsManager() { - for (auto it : mAllMetricProducers) { - for (int atomId : it->getSlicedStateAtoms()) { - StateManager::getInstance().unregisterListener(atomId, it); - } - } - mPullerManager->UnregisterPullUidProvider(mConfigKey, this); - - VLOG("~MetricsManager()"); -} - -bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor) { - vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; - unordered_map<int64_t, int> newAtomMatchingTrackerMap; - vector<sp<ConditionTracker>> newConditionTrackers; - unordered_map<int64_t, int> newConditionTrackerMap; - map<int64_t, uint64_t> newStateProtoHashes; - vector<sp<MetricProducer>> newMetricProducers; - unordered_map<int64_t, int> newMetricProducerMap; - vector<sp<AnomalyTracker>> newAnomalyTrackers; - unordered_map<int64_t, int> newAlertTrackerMap; - vector<sp<AlarmTracker>> newPeriodicAlarmTrackers; - mTagIds.clear(); - mConditionToMetricMap.clear(); - mTrackerToMetricMap.clear(); - mTrackerToConditionMap.clear(); - mActivationAtomTrackerToMetricMap.clear(); - mDeactivationAtomTrackerToMetricMap.clear(); - mMetricIndexesWithActivation.clear(); - mNoReportMetricIds.clear(); - mConfigValid = updateStatsdConfig( - mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, - mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap, - mAllAnomalyTrackers, mAlertTrackerMap, mStateProtoHashes, mTagIds, - newAtomMatchingTrackers, newAtomMatchingTrackerMap, newConditionTrackers, - newConditionTrackerMap, newMetricProducers, newMetricProducerMap, newAnomalyTrackers, - newAlertTrackerMap, newPeriodicAlarmTrackers, mConditionToMetricMap, - mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap, - mDeactivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, newStateProtoHashes, - mNoReportMetricIds); - mAllAtomMatchingTrackers = newAtomMatchingTrackers; - mAtomMatchingTrackerMap = newAtomMatchingTrackerMap; - mAllConditionTrackers = newConditionTrackers; - mConditionTrackerMap = newConditionTrackerMap; - mAllMetricProducers = newMetricProducers; - mMetricProducerMap = newMetricProducerMap; - mStateProtoHashes = newStateProtoHashes; - mAllAnomalyTrackers = newAnomalyTrackers; - mAlertTrackerMap = newAlertTrackerMap; - mAllPeriodicAlarmTrackers = newPeriodicAlarmTrackers; - - mTtlNs = config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1; - refreshTtl(currentTimeNs); - - mHashStringsInReport = config.hash_strings_in_metric_report(); - mVersionStringsInReport = config.version_strings_in_metric_report(); - mInstallerInReport = config.installer_in_metric_report(); - mWhitelistedAtomIds.clear(); - mWhitelistedAtomIds.insert(config.whitelisted_atom_ids().begin(), - config.whitelisted_atom_ids().end()); - mShouldPersistHistory = config.persist_locally(); - - // Store the sub-configs used. - mAnnotations.clear(); - for (const auto& annotation : config.annotation()) { - mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32()); - } - - mAllowedUid.clear(); - mAllowedPkg.clear(); - mDefaultPullUids.clear(); - mPullAtomUids.clear(); - mPullAtomPackages.clear(); - createAllLogSourcesFromConfig(config); - - verifyGuardrailsAndUpdateStatsdStats(); - initializeConfigActiveStatus(); - return mConfigValid; -} - -void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) { - // Init allowed pushed atom uids. - if (config.allowed_log_source_size() == 0) { - mConfigValid = false; - ALOGE("Log source allowlist is empty! This config won't get any data. Suggest adding at " - "least AID_SYSTEM and AID_STATSD to the allowed_log_source field."); - } else { - for (const auto& source : config.allowed_log_source()) { - auto it = UidMap::sAidToUidMapping.find(source); - if (it != UidMap::sAidToUidMapping.end()) { - mAllowedUid.push_back(it->second); - } else { - mAllowedPkg.push_back(source); - } - } - - if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) { - ALOGE("Too many log sources. This is likely to be an error in the config."); - mConfigValid = false; - } else { - initAllowedLogSources(); - } - } - - // Init default allowed pull atom uids. - int numPullPackages = 0; - for (const string& pullSource : config.default_pull_packages()) { - auto it = UidMap::sAidToUidMapping.find(pullSource); - if (it != UidMap::sAidToUidMapping.end()) { - numPullPackages++; - mDefaultPullUids.insert(it->second); - } else { - ALOGE("Default pull atom packages must be in sAidToUidMapping"); - mConfigValid = false; - } - } - // Init per-atom pull atom packages. - for (const PullAtomPackages& pullAtomPackages : config.pull_atom_packages()) { - int32_t atomId = pullAtomPackages.atom_id(); - for (const string& pullPackage : pullAtomPackages.packages()) { - numPullPackages++; - auto it = UidMap::sAidToUidMapping.find(pullPackage); - if (it != UidMap::sAidToUidMapping.end()) { - mPullAtomUids[atomId].insert(it->second); - } else { - mPullAtomPackages[atomId].insert(pullPackage); - } - } - } - if (numPullPackages > StatsdStats::kMaxPullAtomPackages) { - ALOGE("Too many sources in default_pull_packages and pull_atom_packages. This is likely to " - "be an error in the config"); - mConfigValid = false; - } else { - initPullAtomSources(); - } -} - -void MetricsManager::verifyGuardrailsAndUpdateStatsdStats() { - // Guardrail. Reject the config if it's too big. - if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig || - mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig || - mAllAtomMatchingTrackers.size() > StatsdStats::kMaxMatcherCountPerConfig) { - ALOGE("This config is too big! Reject!"); - mConfigValid = false; - } - if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) { - ALOGE("This config has too many alerts! Reject!"); - mConfigValid = false; - } - // no matter whether this config is valid, log it in the stats. - StatsdStats::getInstance().noteConfigReceived( - mConfigKey, mAllMetricProducers.size(), mAllConditionTrackers.size(), - mAllAtomMatchingTrackers.size(), mAllAnomalyTrackers.size(), mAnnotations, - mConfigValid); -} - -void MetricsManager::initializeConfigActiveStatus() { - mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) || - (mAllMetricProducers.size() == 0); - mIsActive = mIsAlwaysActive; - for (int metric : mMetricIndexesWithActivation) { - mIsActive |= mAllMetricProducers[metric]->isActive(); - } - VLOG("mIsActive is initialized to %d", mIsActive); -} - -void MetricsManager::initAllowedLogSources() { - std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); - mAllowedLogSources.clear(); - mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end()); - - for (const auto& pkg : mAllowedPkg) { - auto uids = mUidMap->getAppUid(pkg); - mAllowedLogSources.insert(uids.begin(), uids.end()); - } - if (DEBUG) { - for (const auto& uid : mAllowedLogSources) { - VLOG("Allowed uid %d", uid); - } - } -} - -void MetricsManager::initPullAtomSources() { - std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); - mCombinedPullAtomUids.clear(); - for (const auto& [atomId, uids] : mPullAtomUids) { - mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end()); - } - for (const auto& [atomId, packages] : mPullAtomPackages) { - for (const string& pkg : packages) { - set<int32_t> uids = mUidMap->getAppUid(pkg); - mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end()); - } - } -} - -bool MetricsManager::isConfigValid() const { - return mConfigValid; -} - -void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version) { - // Inform all metric producers. - for (const auto& it : mAllMetricProducers) { - it->notifyAppUpgrade(eventTimeNs); - } - // check if we care this package - if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) { - // We will re-initialize the whole list because we don't want to keep the multi mapping of - // UID<->pkg inside MetricsManager to reduce the memory usage. - initAllowedLogSources(); - } - - for (const auto& it : mPullAtomPackages) { - if (it.second.find(apk) != it.second.end()) { - initPullAtomSources(); - return; - } - } -} - -void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, - const int uid) { - // Inform all metric producers. - for (const auto& it : mAllMetricProducers) { - it->notifyAppRemoved(eventTimeNs); - } - // check if we care this package - if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) { - // We will re-initialize the whole list because we don't want to keep the multi mapping of - // UID<->pkg inside MetricsManager to reduce the memory usage. - initAllowedLogSources(); - } - - for (const auto& it : mPullAtomPackages) { - if (it.second.find(apk) != it.second.end()) { - initPullAtomSources(); - return; - } - } -} - -void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) { - // Purposefully don't inform metric producers on a new snapshot - // because we don't need to flush partial buckets. - // This occurs if a new user is added/removed or statsd crashes. - initPullAtomSources(); - - if (mAllowedPkg.size() == 0) { - return; - } - initAllowedLogSources(); -} - -void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) { - // Inform all metric producers. - for (const auto& it : mAllMetricProducers) { - it->onStatsdInitCompleted(eventTimeNs); - } -} - -void MetricsManager::init() { - for (const auto& producer : mAllMetricProducers) { - producer->prepareFirstBucket(); - } -} - -vector<int32_t> MetricsManager::getPullAtomUids(int32_t atomId) { - std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); - vector<int32_t> uids; - const auto& it = mCombinedPullAtomUids.find(atomId); - if (it != mCombinedPullAtomUids.end()) { - uids.insert(uids.end(), it->second.begin(), it->second.end()); - } - uids.insert(uids.end(), mDefaultPullUids.begin(), mDefaultPullUids.end()); - return uids; -} - -void MetricsManager::dumpStates(FILE* out, bool verbose) { - fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str()); - { - std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); - for (const auto& source : mAllowedLogSources) { - fprintf(out, "%d ", source); - } - } - fprintf(out, "\n"); - for (const auto& producer : mAllMetricProducers) { - producer->dumpStates(out, verbose); - } -} - -void MetricsManager::dropData(const int64_t dropTimeNs) { - for (const auto& producer : mAllMetricProducers) { - producer->dropData(dropTimeNs); - } -} - -void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - ProtoOutputStream* protoOutput) { - VLOG("=========================Metric Reports Start=========================="); - // one StatsLogReport per MetricProduer - for (const auto& producer : mAllMetricProducers) { - if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { - uint64_t token = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS); - if (mHashStringsInReport) { - producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data, - dumpLatency, str_set, protoOutput); - } else { - producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data, - dumpLatency, nullptr, protoOutput); - } - protoOutput->end(token); - } else { - producer->clearPastBuckets(dumpTimeStampNs); - } - } - for (const auto& annotation : mAnnotations) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ANNOTATIONS); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ANNOTATIONS_INT64, - (long long)annotation.first); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ANNOTATIONS_INT32, annotation.second); - protoOutput->end(token); - } - - // Do not update the timestamps when data is not cleared to avoid timestamps from being - // misaligned. - if (erase_data) { - mLastReportTimeNs = dumpTimeStampNs; - mLastReportWallClockNs = getWallClockNs(); - } - VLOG("=========================Metric Reports End=========================="); -} - - -bool MetricsManager::checkLogCredentials(const LogEvent& event) { - if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) { - return true; - } - std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); - if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) { - VLOG("log source %d not on the whitelist", event.GetUid()); - return false; - } - return true; -} - -bool MetricsManager::eventSanityCheck(const LogEvent& event) { - if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) { - // Check that app breadcrumb reported fields are valid. - status_t err = NO_ERROR; - - // Uid is 3rd from last field and must match the caller's uid, - // unless that caller is statsd itself (statsd is allowed to spoof uids). - long appHookUid = event.GetLong(event.size()-2, &err); - if (err != NO_ERROR) { - VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid"); - return false; - } - - // Because the uid within the LogEvent may have been mapped from - // isolated to host, map the loggerUid similarly before comparing. - int32_t loggerUid = mUidMap->getHostUidOrSelf(event.GetUid()); - if (loggerUid != appHookUid && loggerUid != AID_STATSD) { - VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d", - appHookUid, loggerUid); - return false; - } - - // The state must be from 0,3. This part of code must be manually updated. - long appHookState = event.GetLong(event.size(), &err); - if (err != NO_ERROR) { - VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field"); - return false; - } else if (appHookState < 0 || appHookState > 3) { - VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState); - return false; - } - } 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; - - // Uid is the first field provided. - long jankUid = event.GetLong(1, &err); - if (err != NO_ERROR) { - VLOG("Davey occurred had error when parsing the uid"); - return false; - } - int32_t loggerUid = event.GetUid(); - if (loggerUid != jankUid && loggerUid != AID_STATSD) { - VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid, - loggerUid); - return false; - } - - long duration = event.GetLong(event.size(), &err); - if (err != NO_ERROR) { - VLOG("Davey occurred had error when parsing the duration"); - return false; - } else if (duration > 100000) { - VLOG("Davey duration is unreasonably long: %ld", duration); - return false; - } - } - - return true; -} - -// Consume the stats log if it's interesting to this metric. -void MetricsManager::onLogEvent(const LogEvent& event) { - if (!mConfigValid) { - return; - } - - if (!checkLogCredentials(event)) { - return; - } - - if (!eventSanityCheck(event)) { - return; - } - - int tagId = event.GetTagId(); - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - - bool isActive = mIsAlwaysActive; - - // Set of metrics that are still active after flushing. - unordered_set<int> activeMetricsIndices; - - // Update state of all metrics w/ activation conditions as of eventTimeNs. - for (int metricIndex : mMetricIndexesWithActivation) { - const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex]; - metric->flushIfExpire(eventTimeNs); - if (metric->isActive()) { - // If this metric w/ activation condition is still active after - // flushing, remember it. - activeMetricsIndices.insert(metricIndex); - } - } - - mIsActive = isActive || !activeMetricsIndices.empty(); - - if (mTagIds.find(tagId) == mTagIds.end()) { - // Not interesting... - return; - } - - vector<MatchingState> matcherCache(mAllAtomMatchingTrackers.size(), - MatchingState::kNotComputed); - - // Evaluate all atom matchers. - for (auto& matcher : mAllAtomMatchingTrackers) { - matcher->onLogEvent(event, mAllAtomMatchingTrackers, matcherCache); - } - - // Set of metrics that received an activation cancellation. - unordered_set<int> metricIndicesWithCanceledActivations; - - // Determine which metric activations received a cancellation and cancel them. - for (const auto& it : mDeactivationAtomTrackerToMetricMap) { - if (matcherCache[it.first] == MatchingState::kMatched) { - for (int metricIndex : it.second) { - mAllMetricProducers[metricIndex]->cancelEventActivation(it.first); - metricIndicesWithCanceledActivations.insert(metricIndex); - } - } - } - - // Determine whether any metrics are no longer active after cancelling metric activations. - for (const int metricIndex : metricIndicesWithCanceledActivations) { - const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex]; - metric->flushIfExpire(eventTimeNs); - if (!metric->isActive()) { - activeMetricsIndices.erase(metricIndex); - } - } - - isActive |= !activeMetricsIndices.empty(); - - - // Determine which metric activations should be turned on and turn them on - for (const auto& it : mActivationAtomTrackerToMetricMap) { - if (matcherCache[it.first] == MatchingState::kMatched) { - for (int metricIndex : it.second) { - mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs); - isActive |= mAllMetricProducers[metricIndex]->isActive(); - } - } - } - - mIsActive = isActive; - - // A bitmap to see which ConditionTracker needs to be re-evaluated. - vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false); - - for (const auto& pair : mTrackerToConditionMap) { - if (matcherCache[pair.first] == MatchingState::kMatched) { - const auto& conditionList = pair.second; - for (const int conditionIndex : conditionList) { - conditionToBeEvaluated[conditionIndex] = true; - } - } - } - - vector<ConditionState> conditionCache(mAllConditionTrackers.size(), - ConditionState::kNotEvaluated); - // A bitmap to track if a condition has changed value. - vector<bool> changedCache(mAllConditionTrackers.size(), false); - for (size_t i = 0; i < mAllConditionTrackers.size(); i++) { - if (conditionToBeEvaluated[i] == false) { - continue; - } - sp<ConditionTracker>& condition = mAllConditionTrackers[i]; - condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache, - changedCache); - } - - for (size_t i = 0; i < mAllConditionTrackers.size(); i++) { - if (changedCache[i] == false) { - continue; - } - auto pair = mConditionToMetricMap.find(i); - if (pair != mConditionToMetricMap.end()) { - auto& metricList = pair->second; - for (auto metricIndex : metricList) { - // Metric cares about non sliced condition, and it's changed. - // Push the new condition to it directly. - if (!mAllMetricProducers[metricIndex]->isConditionSliced()) { - mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i], - eventTimeNs); - // Metric cares about sliced conditions, and it may have changed. Send - // notification, and the metric can query the sliced conditions that are - // interesting to it. - } else { - mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i], - eventTimeNs); - } - } - } - } - - // For matched AtomMatchers, tell relevant metrics that a matched event has come. - for (size_t i = 0; i < mAllAtomMatchingTrackers.size(); i++) { - if (matcherCache[i] == MatchingState::kMatched) { - StatsdStats::getInstance().noteMatcherMatched(mConfigKey, - mAllAtomMatchingTrackers[i]->getId()); - auto pair = mTrackerToMetricMap.find(i); - if (pair != mTrackerToMetricMap.end()) { - auto& metricList = pair->second; - for (const int metricIndex : metricList) { - // pushed metrics are never scheduled pulls - mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event); - } - } - } - } -} - -void MetricsManager::onAnomalyAlarmFired( - const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) { - for (const auto& itr : mAllAnomalyTrackers) { - itr->informAlarmsFired(timestampNs, alarmSet); - } -} - -void MetricsManager::onPeriodicAlarmFired( - const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) { - for (const auto& itr : mAllPeriodicAlarmTrackers) { - itr->informAlarmsFired(timestampNs, alarmSet); - } -} - -// Returns the total byte size of all metrics managed by a single config source. -size_t MetricsManager::byteSize() { - size_t totalSize = 0; - for (const auto& metricProducer : mAllMetricProducers) { - totalSize += metricProducer->byteSize(); - } - return totalSize; -} - -void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs) { - if (config.metric_size() == 0) { - ALOGW("No active metric for config %s", mConfigKey.ToString().c_str()); - return; - } - - for (int i = 0; i < config.metric_size(); i++) { - const auto& activeMetric = config.metric(i); - for (int metricIndex : mMetricIndexesWithActivation) { - const auto& metric = mAllMetricProducers[metricIndex]; - if (metric->getMetricId() == activeMetric.id()) { - VLOG("Setting active metric: %lld", (long long)metric->getMetricId()); - metric->loadActiveMetric(activeMetric, currentTimeNs); - if (!mIsActive && metric->isActive()) { - StatsdStats::getInstance().noteActiveStatusChanged(mConfigKey, - /*activate=*/ true); - } - mIsActive |= metric->isActive(); - } - } - } -} - -void MetricsManager::writeActiveConfigToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId()); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid()); - for (int metricIndex : mMetricIndexesWithActivation) { - const auto& metric = mAllMetricProducers[metricIndex]; - const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ACTIVE_CONFIG_METRIC); - metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto); - proto->end(metricToken); - } -} - -bool MetricsManager::writeMetadataToProto(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadata* statsMetadata) { - bool metadataWritten = false; - metadata::ConfigKey* configKey = statsMetadata->mutable_config_key(); - configKey->set_config_id(mConfigKey.GetId()); - configKey->set_uid(mConfigKey.GetUid()); - for (const auto& anomalyTracker : mAllAnomalyTrackers) { - metadata::AlertMetadata* alertMetadata = statsMetadata->add_alert_metadata(); - bool alertWritten = anomalyTracker->writeAlertMetadataToProto(currentWallClockTimeNs, - systemElapsedTimeNs, alertMetadata); - if (!alertWritten) { - statsMetadata->mutable_alert_metadata()->RemoveLast(); - } - metadataWritten |= alertWritten; - } - return metadataWritten; -} - -void MetricsManager::loadMetadata(const metadata::StatsMetadata& metadata, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - for (const metadata::AlertMetadata& alertMetadata : metadata.alert_metadata()) { - int64_t alertId = alertMetadata.alert_id(); - auto it = mAlertTrackerMap.find(alertId); - if (it == mAlertTrackerMap.end()) { - ALOGE("No anomalyTracker found for alertId %lld", (long long) alertId); - continue; - } - mAllAnomalyTrackers[it->second]->loadAlertMetadata(alertMetadata, - currentWallClockTimeNs, - systemElapsedTimeNs); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h deleted file mode 100644 index 3c9ecdb46fd2..000000000000 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ /dev/null @@ -1,383 +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. - */ - -#pragma once - -#include "anomaly/AlarmMonitor.h" -#include "anomaly/AlarmTracker.h" -#include "anomaly/AnomalyTracker.h" -#include "condition/ConditionTracker.h" -#include "config/ConfigKey.h" -#include "external/StatsPullerManager.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" -#include "logd/LogEvent.h" -#include "matchers/AtomMatchingTracker.h" -#include "metrics/MetricProducer.h" -#include "packages/UidMap.h" - -#include <unordered_map> - -namespace android { -namespace os { -namespace statsd { - -// A MetricsManager is responsible for managing metrics from one single config source. -class MetricsManager : public virtual android::RefBase, public virtual PullUidProvider { -public: - MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor); - - virtual ~MetricsManager(); - - bool updateConfig(const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor); - - // Return whether the configuration is valid. - bool isConfigValid() const; - - bool checkLogCredentials(const LogEvent& event); - - bool eventSanityCheck(const LogEvent& event); - - void onLogEvent(const LogEvent& event); - - void onAnomalyAlarmFired( - const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet); - - void onPeriodicAlarmFired( - const int64_t& timestampNs, - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet); - - void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version); - - void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid); - - void onUidMapReceived(const int64_t& eventTimeNs); - - void onStatsdInitCompleted(const int64_t& elapsedTimeNs); - - void init(); - - vector<int32_t> getPullAtomUids(int32_t atomId) override; - - bool shouldWriteToDisk() const { - return mNoReportMetricIds.size() != mAllMetricProducers.size(); - } - - bool shouldPersistLocalHistory() const { - return mShouldPersistHistory; - } - - void dumpStates(FILE* out, bool verbose); - - inline bool isInTtl(const int64_t timestampNs) const { - return mTtlNs <= 0 || timestampNs < mTtlEndNs; - }; - - inline bool hashStringInReport() const { - return mHashStringsInReport; - }; - - inline bool versionStringsInReport() const { - return mVersionStringsInReport; - }; - - inline bool installerInReport() const { - return mInstallerInReport; - }; - - void refreshTtl(const int64_t currentTimestampNs) { - if (mTtlNs > 0) { - mTtlEndNs = currentTimestampNs + mTtlNs; - } - }; - - // Returns the elapsed realtime when this metric manager last reported metrics. If this config - // has not yet dumped any reports, this is the time the metricsmanager was initialized. - inline int64_t getLastReportTimeNs() const { - return mLastReportTimeNs; - }; - - inline int64_t getLastReportWallClockNs() const { - return mLastReportWallClockNs; - }; - - inline size_t getNumMetrics() const { - return mAllMetricProducers.size(); - } - - virtual void dropData(const int64_t dropTimeNs); - - virtual void onDumpReport(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - android::util::ProtoOutputStream* protoOutput); - - // Computes the total byte size of all metrics managed by a single config source. - // Does not change the state. - virtual size_t byteSize(); - - // Returns whether or not this config is active. - // The config is active if any metric in the config is active. - inline bool isActive() const { - return mIsActive; - } - - void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs); - - void writeActiveConfigToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); - - // Returns true if at least one piece of metadata is written. - bool writeMetadataToProto(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadata* statsMetadata); - - void loadMetadata(const metadata::StatsMetadata& metadata, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs); -private: - // For test only. - inline int64_t getTtlEndNs() const { return mTtlEndNs; } - - const ConfigKey mConfigKey; - - sp<UidMap> mUidMap; - - bool mConfigValid = false; - - bool mHashStringsInReport = false; - bool mVersionStringsInReport = false; - bool mInstallerInReport = false; - - int64_t mTtlNs; - int64_t mTtlEndNs; - - int64_t mLastReportTimeNs; - int64_t mLastReportWallClockNs; - - sp<StatsPullerManager> mPullerManager; - - // The uid log sources from StatsdConfig. - std::vector<int32_t> mAllowedUid; - - // The pkg log sources from StatsdConfig. - std::vector<std::string> mAllowedPkg; - - // The combined uid sources (after translating pkg name to uid). - // Logs from uids that are not in the list will be ignored to avoid spamming. - std::set<int32_t> mAllowedLogSources; - - // To guard access to mAllowedLogSources - mutable std::mutex mAllowedLogSourcesMutex; - - std::set<int32_t> mWhitelistedAtomIds; - - // We can pull any atom from these uids. - std::set<int32_t> mDefaultPullUids; - - // Uids that specific atoms can pull from. - // This is a map<atom id, set<uids>> - std::map<int32_t, std::set<int32_t>> mPullAtomUids; - - // Packages that specific atoms can be pulled from. - std::map<int32_t, std::set<std::string>> mPullAtomPackages; - - // All uids to pull for this atom. NOTE: Does not include the default uids for memory. - std::map<int32_t, std::set<int32_t>> mCombinedPullAtomUids; - - // Contains the annotations passed in with StatsdConfig. - std::list<std::pair<const int64_t, const int32_t>> mAnnotations; - - bool mShouldPersistHistory; - - // All event tags that are interesting to my metrics. - std::set<int> mTagIds; - - // We only store the sp of AtomMatchingTracker, MetricProducer, and ConditionTracker in - // MetricsManager. There are relationships between them, and the relationships are denoted by - // index instead of pointers. The reasons for this are: (1) the relationship between them are - // complicated, so storing index instead of pointers reduces the risk that A holds B's sp, and B - // holds A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get - // the related results from a cache using the index. - - // Hold all the atom matchers from the config. - std::vector<sp<AtomMatchingTracker>> mAllAtomMatchingTrackers; - - // Hold all the conditions from the config. - std::vector<sp<ConditionTracker>> mAllConditionTrackers; - - // Hold all metrics from the config. - std::vector<sp<MetricProducer>> mAllMetricProducers; - - // Hold all alert trackers. - std::vector<sp<AnomalyTracker>> mAllAnomalyTrackers; - - // Hold all periodic alarm trackers. - std::vector<sp<AlarmTracker>> mAllPeriodicAlarmTrackers; - - // To make updating configs faster, we map the id of a AtomMatchingTracker, MetricProducer, and - // ConditionTracker to its index in the corresponding vector. - - // Maps the id of an atom matching tracker to its index in mAllAtomMatchingTrackers. - std::unordered_map<int64_t, int> mAtomMatchingTrackerMap; - - // Maps the id of a condition tracker to its index in mAllConditionTrackers. - std::unordered_map<int64_t, int> mConditionTrackerMap; - - // Maps the id of a metric producer to its index in mAllMetricProducers. - std::unordered_map<int64_t, int> mMetricProducerMap; - - // To make the log processing more efficient, we want to do as much filtering as possible - // before we go into individual trackers and conditions to match. - - // 1st filter: check if the event tag id is in mTagIds. - // 2nd filter: if it is, we parse the event because there is at least one member is interested. - // then pass to all AtomMatchingTrackers (itself also filter events by ids). - // 3nd filter: for AtomMatchingTrackers that matched this event, we pass this event to the - // ConditionTrackers and MetricProducers that use this matcher. - // 4th filter: for ConditionTrackers that changed value due to this event, we pass - // new conditions to metrics that use this condition. - - // The following map is initialized from the statsd_config. - - // Maps from the index of the AtomMatchingTracker to index of MetricProducer. - std::unordered_map<int, std::vector<int>> mTrackerToMetricMap; - - // Maps from AtomMatchingTracker to ConditionTracker - std::unordered_map<int, std::vector<int>> mTrackerToConditionMap; - - // Maps from ConditionTracker to MetricProducer - std::unordered_map<int, std::vector<int>> mConditionToMetricMap; - - // Maps from life span triggering event to MetricProducers. - std::unordered_map<int, std::vector<int>> mActivationAtomTrackerToMetricMap; - - // Maps deactivation triggering event to MetricProducers. - std::unordered_map<int, std::vector<int>> mDeactivationAtomTrackerToMetricMap; - - // Maps AlertIds to the index of the corresponding AnomalyTracker stored in mAllAnomalyTrackers. - // The map is used in LoadMetadata to more efficiently lookup AnomalyTrackers from an AlertId. - std::unordered_map<int64_t, int> mAlertTrackerMap; - - std::vector<int> mMetricIndexesWithActivation; - - void initAllowedLogSources(); - - void initPullAtomSources(); - - // Only called on config creation/update to initialize log sources from the config. - // Calls initAllowedLogSources and initPullAtomSources. Sets mConfigValid to false on error. - void createAllLogSourcesFromConfig(const StatsdConfig& config); - - // Verifies the config meets guardrails and updates statsdStats. - // Sets mConfigValid to false on error. Should be called on config creation/update - void verifyGuardrailsAndUpdateStatsdStats(); - - // Initializes mIsAlwaysActive and mIsActive. - // Should be called on config creation/update. - void initializeConfigActiveStatus(); - - // The metrics that don't need to be uploaded or even reported. - std::set<int64_t> mNoReportMetricIds; - - // The config is active if any metric in the config is active. - bool mIsActive; - - // The config is always active if any metric in the config does not have an activation signal. - bool mIsAlwaysActive; - - // Hashes of the States used in this config, keyed by the state id, used in config updates. - std::map<int64_t, uint64_t> mStateProtoHashes; - - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); - FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); - FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid); - FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain); - FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition); - FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents); - - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); - - FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); - FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); - FRIEND_TEST(ConfigUpdateE2eTest, TestConfigTtl); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); - - FRIEND_TEST(MetricsManagerTest, TestLogSources); - FRIEND_TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate); - - FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); - FRIEND_TEST(StatsLogProcessorTest, - TestActivationOnBootMultipleActivationsDifferentActivationTypes); - FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); - - FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges); - FRIEND_TEST(CountMetricE2eTest, TestSlicedState); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); - FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields); - - FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); - FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); - FRIEND_TEST(DurationMetricE2eTest, TestWithCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSuperset); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset); - - FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp deleted file mode 100644 index 22fdf1604435..000000000000 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ /dev/null @@ -1,1282 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "ValueMetricProducer.h" - -#include <limits.h> -#include <stdlib.h> - -#include "../guardrail/StatsdStats.h" -#include "../stats_log_util.h" -#include "metrics/parsing_utils/metrics_manager_util.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_DOUBLE; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::map; -using std::shared_ptr; -using std::unordered_map; - -namespace android { -namespace os { -namespace statsd { - -// for StatsLogReport -const int FIELD_ID_ID = 1; -const int FIELD_ID_VALUE_METRICS = 7; -const int FIELD_ID_TIME_BASE = 9; -const int FIELD_ID_BUCKET_SIZE = 10; -const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; -const int FIELD_ID_IS_ACTIVE = 14; -// for ValueMetricDataWrapper -const int FIELD_ID_DATA = 1; -const int FIELD_ID_SKIPPED = 2; -// for SkippedBuckets -const int FIELD_ID_SKIPPED_START_MILLIS = 3; -const int FIELD_ID_SKIPPED_END_MILLIS = 4; -const int FIELD_ID_SKIPPED_DROP_EVENT = 5; -// for DumpEvent Proto -const int FIELD_ID_BUCKET_DROP_REASON = 1; -const int FIELD_ID_DROP_TIME = 2; -// for ValueMetricData -const int FIELD_ID_DIMENSION_IN_WHAT = 1; -const int FIELD_ID_BUCKET_INFO = 3; -const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4; -const int FIELD_ID_SLICE_BY_STATE = 6; -// for ValueBucketInfo -const int FIELD_ID_VALUE_INDEX = 1; -const int FIELD_ID_VALUE_LONG = 2; -const int FIELD_ID_VALUE_DOUBLE = 3; -const int FIELD_ID_VALUES = 9; -const int FIELD_ID_BUCKET_NUM = 4; -const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5; -const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; -const int FIELD_ID_CONDITION_TRUE_NS = 10; - -const Value ZERO_LONG((int64_t)0); -const Value ZERO_DOUBLE((int64_t)0); - -// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently -ValueMetricProducer::ValueMetricProducer( - const ConfigKey& key, const ValueMetric& metric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, - const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash, - const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, - const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs, - const sp<StatsPullerManager>& pullerManager, - const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, - const vector<int>& slicedStateAtoms, - const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, - conditionWizard, protoHash, eventActivationMap, eventDeactivationMap, - slicedStateAtoms, stateGroupMap), - mWhatMatcherIndex(whatMatcherIndex), - mEventMatcherWizard(matcherWizard), - mPullerManager(pullerManager), - mPullTagId(pullTagId), - mIsPulled(pullTagId != -1), - mMinBucketSizeNs(metric.min_bucket_size_nanos()), - mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) != - StatsdStats::kAtomDimensionKeySizeLimitMap.end() - ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first - : StatsdStats::kDimensionKeySizeSoftLimit), - mDimensionHardLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) != - StatsdStats::kAtomDimensionKeySizeLimitMap.end() - ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second - : StatsdStats::kDimensionKeySizeHardLimit), - mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()), - mAggregationType(metric.aggregation_type()), - mUseDiff(metric.has_use_diff() ? metric.use_diff() : (mIsPulled ? true : false)), - mValueDirection(metric.value_direction()), - mSkipZeroDiffOutput(metric.skip_zero_diff_output()), - mUseZeroDefaultBase(metric.use_zero_default_base()), - mHasGlobalBase(false), - mCurrentBucketIsSkipped(false), - mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC - : StatsdStats::kPullMaxDelayNs), - mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()), - // Condition timer will be set later within the constructor after pulling events - mConditionTimer(false, timeBaseNs) { - int64_t bucketSizeMills = 0; - if (metric.has_bucket()) { - bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()); - } else { - bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR); - } - - mBucketSizeNs = bucketSizeMills * 1000000; - - translateFieldMatcher(metric.value_field(), &mFieldMatchers); - - if (metric.has_dimensions_in_what()) { - translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); - mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); - mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()); - } - - if (metric.links().size() > 0) { - for (const auto& link : metric.links()) { - Metric2Condition mc; - mc.conditionId = link.condition(); - translateFieldMatcher(link.fields_in_what(), &mc.metricFields); - translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); - mMetric2ConditionLinks.push_back(mc); - } - mConditionSliced = true; - } - - for (const auto& stateLink : metric.state_link()) { - Metric2State ms; - ms.stateAtomId = stateLink.state_atom_id(); - translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields); - translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields); - mMetric2StateLinks.push_back(ms); - } - - int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs); - mCurrentBucketNum += numBucketsForward; - - flushIfNeededLocked(startTimeNs); - - if (mIsPulled) { - mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(), - mBucketSizeNs); - } - - // Only do this for partial buckets like first bucket. All other buckets should use - // flushIfNeeded to adjust start and end to bucket boundaries. - // Adjust start for partial bucket - mCurrentBucketStartTimeNs = startTimeNs; - mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs); - - // Now that activations are processed, start the condition timer if needed. - mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue, - mCurrentBucketStartTimeNs); - - VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), - (long long)mBucketSizeNs, (long long)mTimeBaseNs); -} - -ValueMetricProducer::~ValueMetricProducer() { - VLOG("~ValueMetricProducer() called"); - if (mIsPulled) { - mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this); - } -} - -bool ValueMetricProducer::onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - if (!MetricProducer::onConfigUpdatedLocked( - config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return false; - } - - const ValueMetric& metric = config.value_metric(configIndex); - // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps. - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, mWhatMatcherIndex)) { - return false; - } - - if (metric.has_condition() && - !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, mConditionTrackerIndex, - conditionToMetricMap)) { - return false; - } - sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard; - mEventMatcherWizard = matcherWizard; - return true; -} - -void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId, - const HashableDimensionKey& primaryKey, - const FieldValue& oldState, const FieldValue& newState) { - VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d", - (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(), - oldState.mValue.int_value, newState.mValue.int_value); - - // If old and new states are in the same StateGroup, then we do not need to - // pull for this state change. - FieldValue oldStateCopy = oldState; - FieldValue newStateCopy = newState; - mapStateValue(atomId, &oldStateCopy); - mapStateValue(atomId, &newStateCopy); - if (oldStateCopy == newStateCopy) { - return; - } - - // If condition is not true or metric is not active, we do not need to pull - // for this state change. - if (mCondition != ConditionState::kTrue || !mIsActive) { - return; - } - - bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs; - if (isEventLate) { - VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, - (long long)mCurrentBucketStartTimeNs); - invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET); - return; - } - mStateChangePrimaryKey.first = atomId; - mStateChangePrimaryKey.second = primaryKey; - if (mIsPulled) { - pullAndMatchEventsLocked(eventTimeNs); - } - mStateChangePrimaryKey.first = 0; - mStateChangePrimaryKey.second = DEFAULT_DIMENSION_KEY; - flushIfNeededLocked(eventTimeNs); -} - -void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) { - VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); -} - -void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - StatsdStats::getInstance().noteBucketDropped(mMetricId); - - // The current partial bucket is not flushed and does not require a pull, - // so the data is still valid. - flushIfNeededLocked(dropTimeNs); - clearPastBucketsLocked(dropTimeNs); -} - -void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - mPastBuckets.clear(); - mSkippedBuckets.clear(); -} - -void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - ProtoOutputStream* protoOutput) { - VLOG("metric %lld dump report now...", (long long)mMetricId); - if (include_current_partial_bucket) { - // For pull metrics, we need to do a pull at bucket boundaries. If we do not do that the - // current bucket will have incomplete data and the next will have the wrong snapshot to do - // a diff against. If the condition is false, we are fine since the base data is reset and - // we are not tracking anything. - bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue; - if (pullNeeded) { - switch (dumpLatency) { - case FAST: - invalidateCurrentBucket(dumpTimeNs, BucketDropReason::DUMP_REPORT_REQUESTED); - break; - case NO_TIME_CONSTRAINTS: - pullAndMatchEventsLocked(dumpTimeNs); - break; - } - } - flushCurrentBucketLocked(dumpTimeNs, dumpTimeNs); - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); - - if (mPastBuckets.empty() && mSkippedBuckets.empty()) { - return; - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs); - // Fills the dimension path if not slicing by ALL. - if (!mSliceByPositionALL) { - if (!mDimensionsInWhat.empty()) { - uint64_t dimenPathToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT); - writeDimensionPathToProto(mDimensionsInWhat, protoOutput); - protoOutput->end(dimenPathToken); - } - } - - uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS); - - for (const auto& skippedBucket : mSkippedBuckets) { - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS, - (long long)(NanoToMillis(skippedBucket.bucketStartTimeNs))); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS, - (long long)(NanoToMillis(skippedBucket.bucketEndTimeNs))); - for (const auto& dropEvent : skippedBucket.dropEvents) { - uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SKIPPED_DROP_EVENT); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME, - (long long)(NanoToMillis(dropEvent.dropTimeNs))); - protoOutput->end(dropEventToken); - } - protoOutput->end(wrapperToken); - } - - for (const auto& pair : mPastBuckets) { - const MetricDimensionKey& dimensionKey = pair.first; - VLOG(" dimension key %s", dimensionKey.toString().c_str()); - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - - // First fill dimension. - if (mSliceByPositionALL) { - uint64_t dimensionToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput); - protoOutput->end(dimensionToken); - } else { - writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(), - FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput); - } - - // Then fill slice_by_state. - for (auto state : dimensionKey.getStateValuesKey().getValues()) { - uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SLICE_BY_STATE); - writeStateToProto(state, protoOutput); - protoOutput->end(stateToken); - } - - // Then fill bucket_info (ValueBucketInfo). - for (const auto& bucket : pair.second) { - uint64_t bucketInfoToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - - if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketStartNs)); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketEndNs)); - } else { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM, - (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs))); - } - // We only write the condition timer value if the metric has a - // condition and/or is sliced by state. - // If the metric is sliced by state, the condition timer value is - // also sliced by state to reflect time spent in that state. - if (mConditionTrackerIndex >= 0 || !mSlicedStateAtoms.empty()) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS, - (long long)bucket.mConditionTrueNs); - } - for (int i = 0; i < (int)bucket.valueIndex.size(); i++) { - int index = bucket.valueIndex[i]; - const Value& value = bucket.values[i]; - uint64_t valueToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_VALUES); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_INDEX, - index); - if (value.getType() == LONG) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_LONG, - (long long)value.long_value); - VLOG("\t bucket [%lld - %lld] value %d: %lld", (long long)bucket.mBucketStartNs, - (long long)bucket.mBucketEndNs, index, (long long)value.long_value); - } else if (value.getType() == DOUBLE) { - protoOutput->write(FIELD_TYPE_DOUBLE | FIELD_ID_VALUE_DOUBLE, - value.double_value); - VLOG("\t bucket [%lld - %lld] value %d: %.2f", (long long)bucket.mBucketStartNs, - (long long)bucket.mBucketEndNs, index, value.double_value); - } else { - VLOG("Wrong value type for ValueMetric output: %d", value.getType()); - } - protoOutput->end(valueToken); - } - protoOutput->end(bucketInfoToken); - } - protoOutput->end(wrapperToken); - } - protoOutput->end(protoToken); - - VLOG("metric %lld dump report now...", (long long)mMetricId); - if (erase_data) { - mPastBuckets.clear(); - mSkippedBuckets.clear(); - } -} - -void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs, - const BucketDropReason reason) { - if (!mCurrentBucketIsSkipped) { - // Only report to StatsdStats once per invalid bucket. - StatsdStats::getInstance().noteInvalidatedBucket(mMetricId); - } - - skipCurrentBucket(dropTimeNs, reason); -} - -void ValueMetricProducer::invalidateCurrentBucket(const int64_t dropTimeNs, - const BucketDropReason reason) { - invalidateCurrentBucketWithoutResetBase(dropTimeNs, reason); - resetBase(); -} - -void ValueMetricProducer::skipCurrentBucket(const int64_t dropTimeNs, - const BucketDropReason reason) { - if (!maxDropEventsReached()) { - mCurrentSkippedBucket.dropEvents.emplace_back(buildDropEvent(dropTimeNs, reason)); - } - mCurrentBucketIsSkipped = true; -} - -void ValueMetricProducer::resetBase() { - for (auto& slice : mCurrentBaseInfo) { - for (auto& baseInfo : slice.second.baseInfos) { - baseInfo.hasBase = false; - } - } - mHasGlobalBase = false; -} - -// Handle active state change. Active state change is treated like a condition change: -// - drop bucket if active state change event arrives too late -// - if condition is true, pull data on active state changes -// - ConditionTimer tracks changes based on AND of condition and active state. -void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) { - bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs; - if (isEventTooLate) { - // Drop bucket because event arrived too late, ie. we are missing data for this bucket. - StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); - invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET); - } - - // Call parent method once we've verified the validity of current bucket. - MetricProducer::onActiveStateChangedLocked(eventTimeNs); - - if (ConditionState::kTrue != mCondition) { - return; - } - - // Pull on active state changes. - if (!isEventTooLate) { - if (mIsPulled) { - pullAndMatchEventsLocked(eventTimeNs); - } - // When active state changes from true to false, clear diff base but don't - // reset other counters as we may accumulate more value in the bucket. - if (mUseDiff && !mIsActive) { - resetBase(); - } - } - - flushIfNeededLocked(eventTimeNs); - - // Let condition timer know of new active state. - mConditionTimer.onConditionChanged(mIsActive, eventTimeNs); - - updateCurrentSlicedBucketConditionTimers(mIsActive, eventTimeNs); -} - -void ValueMetricProducer::onConditionChangedLocked(const bool condition, - const int64_t eventTimeNs) { - ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse; - bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs; - - // If the config is not active, skip the event. - if (!mIsActive) { - mCondition = isEventTooLate ? ConditionState::kUnknown : newCondition; - return; - } - - // If the event arrived late, mark the bucket as invalid and skip the event. - if (isEventTooLate) { - VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, - (long long)mCurrentBucketStartTimeNs); - StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); - StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId); - invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET); - mCondition = ConditionState::kUnknown; - mConditionTimer.onConditionChanged(mCondition, eventTimeNs); - - updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs); - return; - } - - // If the previous condition was unknown, mark the bucket as invalid - // because the bucket will contain partial data. For example, the condition - // change might happen close to the end of the bucket and we might miss a - // lot of data. - // - // We still want to pull to set the base. - if (mCondition == ConditionState::kUnknown) { - invalidateCurrentBucket(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN); - } - - // Pull and match for the following condition change cases: - // unknown/false -> true - condition changed - // true -> false - condition changed - // true -> true - old condition was true so we can flush the bucket at the - // end if needed. - // - // We don’t need to pull for unknown -> false or false -> false. - // - // onConditionChangedLocked might happen on bucket boundaries if this is - // called before #onDataPulled. - if (mIsPulled && - (newCondition == ConditionState::kTrue || mCondition == ConditionState::kTrue)) { - pullAndMatchEventsLocked(eventTimeNs); - } - - // For metrics that use diff, when condition changes from true to false, - // clear diff base but don't reset other counts because we may accumulate - // more value in the bucket. - if (mUseDiff && - (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)) { - resetBase(); - } - - // Update condition state after pulling. - mCondition = newCondition; - - flushIfNeededLocked(eventTimeNs); - mConditionTimer.onConditionChanged(mCondition, eventTimeNs); - - updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs); -} - -void ValueMetricProducer::updateCurrentSlicedBucketConditionTimers(bool newCondition, - int64_t eventTimeNs) { - if (mSlicedStateAtoms.empty()) { - return; - } - - // Utilize the current state key of each DimensionsInWhat key to determine - // which condition timers to update. - // - // Assumes that the MetricDimensionKey exists in `mCurrentSlicedBucket`. - bool inPulledData; - for (const auto& [dimensionInWhatKey, dimensionInWhatInfo] : mCurrentBaseInfo) { - // If the new condition is true, turn ON the condition timer only if - // the DimensionInWhat key was present in the pulled data. - inPulledData = dimensionInWhatInfo.hasCurrentState; - mCurrentSlicedBucket[MetricDimensionKey(dimensionInWhatKey, - dimensionInWhatInfo.currentState)] - .conditionTimer.onConditionChanged(newCondition && inPulledData, eventTimeNs); - } -} - -void ValueMetricProducer::prepareFirstBucketLocked() { - // Kicks off the puller immediately if condition is true and diff based. - if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { - pullAndMatchEventsLocked(mCurrentBucketStartTimeNs); - } -} - -void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { - vector<std::shared_ptr<LogEvent>> allData; - if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) { - ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs); - invalidateCurrentBucket(timestampNs, BucketDropReason::PULL_FAILED); - return; - } - - accumulateEvents(allData, timestampNs, timestampNs); -} - -int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) { - return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs; -} - -// By design, statsd pulls data at bucket boundaries using AlarmManager. These pulls are likely -// to be delayed. Other events like condition changes or app upgrade which are not based on -// AlarmManager might have arrived earlier and close the bucket. -void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, - bool pullSuccess, int64_t originalPullTimeNs) { - std::lock_guard<std::mutex> lock(mMutex); - if (mCondition == ConditionState::kTrue) { - // If the pull failed, we won't be able to compute a diff. - if (!pullSuccess) { - invalidateCurrentBucket(originalPullTimeNs, BucketDropReason::PULL_FAILED); - } else { - bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs(); - if (isEventLate) { - // If the event is late, we are in the middle of a bucket. Just - // process the data without trying to snap the data to the nearest bucket. - accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs); - } else { - // For scheduled pulled data, the effective event time is snap to the nearest - // bucket end. In the case of waking up from a deep sleep state, we will - // attribute to the previous bucket end. If the sleep was long but not very - // long, we will be in the immediate next bucket. Previous bucket may get a - // larger number as we pull at a later time than real bucket end. - // - // If the sleep was very long, we skip more than one bucket before sleep. In - // this case, if the diff base will be cleared and this new data will serve as - // new diff base. - int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1; - StatsdStats::getInstance().noteBucketBoundaryDelayNs( - mMetricId, originalPullTimeNs - bucketEndTime); - accumulateEvents(allData, originalPullTimeNs, bucketEndTime); - } - } - } - - // We can probably flush the bucket. Since we used bucketEndTime when calling - // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed. - flushIfNeededLocked(originalPullTimeNs); -} - -void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, - int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) { - bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs; - if (isEventLate) { - VLOG("Skip bucket end pull due to late arrival: %lld vs %lld", - (long long)eventElapsedTimeNs, (long long)mCurrentBucketStartTimeNs); - StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); - invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET); - return; - } - - const int64_t elapsedRealtimeNs = getElapsedRealtimeNs(); - const int64_t pullDelayNs = elapsedRealtimeNs - originalPullTimeNs; - StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs); - if (pullDelayNs > mMaxPullDelayNs) { - ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId, - (long long)mMaxPullDelayNs); - StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId); - // We are missing one pull from the bucket which means we will not have a complete view of - // what's going on. - invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::PULL_DELAYED); - return; - } - - mMatchedMetricDimensionKeys.clear(); - for (const auto& data : allData) { - LogEvent localCopy = data->makeCopy(); - if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) == - MatchingState::kMatched) { - localCopy.setElapsedTimestampNs(eventElapsedTimeNs); - onMatchedLogEventLocked(mWhatMatcherIndex, localCopy); - } - } - // If a key that is: - // 1. Tracked in mCurrentSlicedBucket and - // 2. A superset of the current mStateChangePrimaryKey - // was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys) - // then we need to reset the base. - for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) { - const auto& whatKey = metricDimensionKey.getDimensionKeyInWhat(); - bool presentInPulledData = - mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end(); - if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) { - auto it = mCurrentBaseInfo.find(whatKey); - for (auto& baseInfo : it->second.baseInfos) { - baseInfo.hasBase = false; - } - // Set to false when DimensionInWhat key is not present in a pull. - // Used in onMatchedLogEventInternalLocked() to ensure the condition - // timer is turned on the next pull when data is present. - it->second.hasCurrentState = false; - // Turn OFF condition timer for keys not present in pulled data. - currentValueBucket.conditionTimer.onConditionChanged(false, eventElapsedTimeNs); - } - } - mMatchedMetricDimensionKeys.clear(); - mHasGlobalBase = true; - - // If we reach the guardrail, we might have dropped some data which means the bucket is - // incomplete. - // - // The base also needs to be reset. If we do not have the full data, we might - // incorrectly compute the diff when mUseZeroDefaultBase is true since an existing key - // might be missing from mCurrentSlicedBucket. - if (hasReachedGuardRailLimit()) { - invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::DIMENSION_GUARDRAIL_REACHED); - mCurrentSlicedBucket.clear(); - } -} - -void ValueMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { - if (mCurrentSlicedBucket.size() == 0) { - return; - } - - fprintf(out, "ValueMetric %lld dimension size %lu\n", (long long)mMetricId, - (unsigned long)mCurrentSlicedBucket.size()); - if (verbose) { - for (const auto& it : mCurrentSlicedBucket) { - for (const auto& interval : it.second.intervals) { - fprintf(out, "\t(what)%s\t(states)%s (value)%s\n", - it.first.getDimensionKeyInWhat().toString().c_str(), - it.first.getStateValuesKey().toString().c_str(), - interval.value.toString().c_str()); - } - } - } -} - -bool ValueMetricProducer::hasReachedGuardRailLimit() const { - return mCurrentSlicedBucket.size() >= mDimensionHardLimit; -} - -bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { - // ===========GuardRail============== - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) { - return false; - } - if (mCurrentSlicedBucket.size() > mDimensionSoftLimit - 1) { - size_t newTupleCount = mCurrentSlicedBucket.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (hasReachedGuardRailLimit()) { - ALOGE("ValueMetric %lld dropping data for dimension key %s", (long long)mMetricId, - newKey.toString().c_str()); - StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); - return true; - } - } - - return false; -} - -bool ValueMetricProducer::hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey) { - // ===========GuardRail============== - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentFullBucket.find(newKey) != mCurrentFullBucket.end()) { - return false; - } - if (mCurrentFullBucket.size() > mDimensionSoftLimit - 1) { - size_t newTupleCount = mCurrentFullBucket.size() + 1; - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > mDimensionHardLimit) { - ALOGE("ValueMetric %lld dropping data for full bucket dimension key %s", - (long long)mMetricId, - newKey.toString().c_str()); - return true; - } - } - - return false; -} - -bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret) { - for (const FieldValue& value : event.getValues()) { - if (value.mField.matches(matcher)) { - switch (value.mValue.type) { - case INT: - ret.setLong(value.mValue.int_value); - break; - case LONG: - ret.setLong(value.mValue.long_value); - break; - case FLOAT: - ret.setDouble(value.mValue.float_value); - break; - case DOUBLE: - ret.setDouble(value.mValue.double_value); - break; - default: - return false; - break; - } - return true; - } - } - return false; -} - -bool ValueMetricProducer::multipleBucketsSkipped(const int64_t numBucketsForward) { - // Skip buckets if this is a pulled metric or a pushed metric that is diffed. - return numBucketsForward > 1 && (mIsPulled || mUseDiff); -} - -void ValueMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const map<int, HashableDimensionKey>& statePrimaryKeys) { - auto whatKey = eventKey.getDimensionKeyInWhat(); - auto stateKey = eventKey.getStateValuesKey(); - - // Skip this event if a state changed occurred for a different primary key. - auto it = statePrimaryKeys.find(mStateChangePrimaryKey.first); - // Check that both the atom id and the primary key are equal. - if (it != statePrimaryKeys.end() && it->second != mStateChangePrimaryKey.second) { - VLOG("ValueMetric skip event with primary key %s because state change primary key " - "is %s", - it->second.toString().c_str(), mStateChangePrimaryKey.second.toString().c_str()); - return; - } - - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - if (eventTimeNs < mCurrentBucketStartTimeNs) { - VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, - (long long)mCurrentBucketStartTimeNs); - return; - } - mMatchedMetricDimensionKeys.insert(whatKey); - - if (!mIsPulled) { - // We cannot flush without doing a pull first. - flushIfNeededLocked(eventTimeNs); - } - - // We should not accumulate the data for pushed metrics when the condition is false. - bool shouldSkipForPushMetric = !mIsPulled && !condition; - // For pulled metrics, there are two cases: - // - to compute diffs, we need to process all the state changes - // - for non-diffs metrics, we should ignore the data if the condition wasn't true. If we have a - // state change from - // + True -> True: we should process the data, it might be a bucket boundary - // + True -> False: we als need to process the data. - bool shouldSkipForPulledMetric = mIsPulled && !mUseDiff - && mCondition != ConditionState::kTrue; - if (shouldSkipForPushMetric || shouldSkipForPulledMetric) { - VLOG("ValueMetric skip event because condition is false and we are not using diff (for " - "pulled metric)"); - return; - } - - if (hitGuardRailLocked(eventKey)) { - return; - } - - const auto& returnVal = - mCurrentBaseInfo.emplace(whatKey, DimensionsInWhatInfo(getUnknownStateKey())); - DimensionsInWhatInfo& dimensionsInWhatInfo = returnVal.first->second; - const HashableDimensionKey oldStateKey = dimensionsInWhatInfo.currentState; - vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos; - if (baseInfos.size() < mFieldMatchers.size()) { - VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size()); - baseInfos.resize(mFieldMatchers.size()); - } - - // Ensure we turn on the condition timer in the case where dimensions - // were missing on a previous pull due to a state change. - bool stateChange = oldStateKey != stateKey; - if (!dimensionsInWhatInfo.hasCurrentState) { - stateChange = true; - dimensionsInWhatInfo.hasCurrentState = true; - } - - // We need to get the intervals stored with the previous state key so we can - // close these value intervals. - vector<Interval>& intervals = - mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals; - if (intervals.size() < mFieldMatchers.size()) { - VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size()); - intervals.resize(mFieldMatchers.size()); - } - - // We only use anomaly detection under certain cases. - // N.B.: The anomaly detection cases were modified in order to fix an issue with value metrics - // containing multiple values. We tried to retain all previous behaviour, but we are unsure the - // previous behaviour was correct. At the time of the fix, anomaly detection had no owner. - // Whoever next works on it should look into the cases where it is triggered in this function. - // Discussion here: http://ag/6124370. - bool useAnomalyDetection = true; - - dimensionsInWhatInfo.hasCurrentState = true; - dimensionsInWhatInfo.currentState = stateKey; - for (int i = 0; i < (int)mFieldMatchers.size(); i++) { - const Matcher& matcher = mFieldMatchers[i]; - BaseInfo& baseInfo = baseInfos[i]; - Interval& interval = intervals[i]; - interval.valueIndex = i; - Value value; - if (!getDoubleOrLong(event, matcher, value)) { - VLOG("Failed to get value %d from event %s", i, event.ToString().c_str()); - StatsdStats::getInstance().noteBadValueType(mMetricId); - return; - } - interval.seenNewData = true; - - if (mUseDiff) { - if (!baseInfo.hasBase) { - if (mHasGlobalBase && mUseZeroDefaultBase) { - // The bucket has global base. This key does not. - // Optionally use zero as base. - baseInfo.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE); - baseInfo.hasBase = true; - } else { - // no base. just update base and return. - baseInfo.base = value; - baseInfo.hasBase = true; - // If we're missing a base, do not use anomaly detection on incomplete data - useAnomalyDetection = false; - // Continue (instead of return) here in order to set baseInfo.base and - // baseInfo.hasBase for other baseInfos - continue; - } - } - - Value diff; - switch (mValueDirection) { - case ValueMetric::INCREASING: - if (value >= baseInfo.base) { - diff = value - baseInfo.base; - } else if (mUseAbsoluteValueOnReset) { - diff = value; - } else { - VLOG("Unexpected decreasing value"); - StatsdStats::getInstance().notePullDataError(mPullTagId); - baseInfo.base = value; - // If we've got bad data, do not use anomaly detection - useAnomalyDetection = false; - continue; - } - break; - case ValueMetric::DECREASING: - if (baseInfo.base >= value) { - diff = baseInfo.base - value; - } else if (mUseAbsoluteValueOnReset) { - diff = value; - } else { - VLOG("Unexpected increasing value"); - StatsdStats::getInstance().notePullDataError(mPullTagId); - baseInfo.base = value; - // If we've got bad data, do not use anomaly detection - useAnomalyDetection = false; - continue; - } - break; - case ValueMetric::ANY: - diff = value - baseInfo.base; - break; - default: - break; - } - baseInfo.base = value; - value = diff; - } - - if (interval.hasValue) { - switch (mAggregationType) { - case ValueMetric::SUM: - // for AVG, we add up and take average when flushing the bucket - case ValueMetric::AVG: - interval.value += value; - break; - case ValueMetric::MIN: - interval.value = std::min(value, interval.value); - break; - case ValueMetric::MAX: - interval.value = std::max(value, interval.value); - break; - default: - break; - } - } else { - interval.value = value; - interval.hasValue = true; - } - interval.sampleSize += 1; - } - - // State change. - if (!mSlicedStateAtoms.empty() && stateChange) { - // Turn OFF the condition timer for the previous state key. - mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)] - .conditionTimer.onConditionChanged(false, eventTimeNs); - - // Turn ON the condition timer for the new state key. - mCurrentSlicedBucket[MetricDimensionKey(whatKey, stateKey)] - .conditionTimer.onConditionChanged(true, eventTimeNs); - } - - // Only trigger the tracker if all intervals are correct and we have not skipped the bucket due - // to MULTIPLE_BUCKETS_SKIPPED. - if (useAnomalyDetection && !multipleBucketsSkipped(calcBucketsForwardCount(eventTimeNs))) { - // TODO: propgate proper values down stream when anomaly support doubles - long wholeBucketVal = intervals[0].value.long_value; - auto prev = mCurrentFullBucket.find(eventKey); - if (prev != mCurrentFullBucket.end()) { - wholeBucketVal += prev->second; - } - for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey, - wholeBucketVal); - } - } -} - -// For pulled metrics, we always need to make sure we do a pull before flushing the bucket -// if mCondition is true! -void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { - int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - if (eventTimeNs < currentBucketEndTimeNs) { - VLOG("eventTime is %lld, less than current bucket end time %lld", (long long)eventTimeNs, - (long long)(currentBucketEndTimeNs)); - return; - } - int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs); - int64_t nextBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; - flushCurrentBucketLocked(eventTimeNs, nextBucketStartTimeNs); -} - -int64_t ValueMetricProducer::calcBucketsForwardCount(const int64_t& eventTimeNs) const { - int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - if (eventTimeNs < currentBucketEndTimeNs) { - return 0; - } - return 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; -} - -void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) { - if (mCondition == ConditionState::kUnknown) { - StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId); - invalidateCurrentBucketWithoutResetBase(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN); - } - - VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, - (int)mCurrentSlicedBucket.size()); - - int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); - int64_t bucketEndTime = fullBucketEndTimeNs; - int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs); - - if (multipleBucketsSkipped(numBucketsForward)) { - VLOG("Skipping forward %lld buckets", (long long)numBucketsForward); - StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId); - // Something went wrong. Maybe the device was sleeping for a long time. It is better - // to mark the current bucket as invalid. The last pull might have been successful through. - invalidateCurrentBucketWithoutResetBase(eventTimeNs, - BucketDropReason::MULTIPLE_BUCKETS_SKIPPED); - // End the bucket at the next bucket start time so the entire interval is skipped. - bucketEndTime = nextBucketStartTimeNs; - } else if (eventTimeNs < fullBucketEndTimeNs) { - bucketEndTime = eventTimeNs; - } - - // Close the current bucket. - int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime); - bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs; - if (!isBucketLargeEnough) { - skipCurrentBucket(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL); - } - if (!mCurrentBucketIsSkipped) { - bool bucketHasData = false; - // The current bucket is large enough to keep. - for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) { - PastValueBucket bucket = - buildPartialBucket(bucketEndTime, currentValueBucket.intervals); - if (!mSlicedStateAtoms.empty()) { - bucket.mConditionTrueNs = - currentValueBucket.conditionTimer.newBucketStart(bucketEndTime); - } else { - bucket.mConditionTrueNs = conditionTrueDuration; - } - // it will auto create new vector of ValuebucketInfo if the key is not found. - if (bucket.valueIndex.size() > 0) { - auto& bucketList = mPastBuckets[metricDimensionKey]; - bucketList.push_back(bucket); - bucketHasData = true; - } - } - if (!bucketHasData) { - skipCurrentBucket(eventTimeNs, BucketDropReason::NO_DATA); - } - } - - if (mCurrentBucketIsSkipped) { - mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs; - mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime; - mSkippedBuckets.emplace_back(mCurrentSkippedBucket); - } - - // This means that the current bucket was not flushed before a forced bucket split. - // This can happen if an app update or a dump report with include_current_partial_bucket is - // requested before we get a chance to flush the bucket due to receiving new data, either from - // the statsd socket or the StatsPullerManager. - if (bucketEndTime < nextBucketStartTimeNs) { - SkippedBucket bucketInGap; - bucketInGap.bucketStartTimeNs = bucketEndTime; - bucketInGap.bucketEndTimeNs = nextBucketStartTimeNs; - bucketInGap.dropEvents.emplace_back( - buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA)); - mSkippedBuckets.emplace_back(bucketInGap); - } - appendToFullBucket(eventTimeNs > fullBucketEndTimeNs); - initCurrentSlicedBucket(nextBucketStartTimeNs); - // Update the condition timer again, in case we skipped buckets. - mConditionTimer.newBucketStart(nextBucketStartTimeNs); - - // NOTE: Update the condition timers in `mCurrentSlicedBucket` only when slicing - // by state. Otherwise, the "global" condition timer will be used. - if (!mSlicedStateAtoms.empty()) { - for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) { - currentValueBucket.conditionTimer.newBucketStart(nextBucketStartTimeNs); - } - } - mCurrentBucketNum += numBucketsForward; -} - -PastValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime, - const std::vector<Interval>& intervals) { - PastValueBucket bucket; - bucket.mBucketStartNs = mCurrentBucketStartTimeNs; - bucket.mBucketEndNs = bucketEndTime; - for (const auto& interval : intervals) { - if (interval.hasValue) { - // skip the output if the diff is zero - if (mSkipZeroDiffOutput && mUseDiff && interval.value.isZero()) { - continue; - } - bucket.valueIndex.push_back(interval.valueIndex); - if (mAggregationType != ValueMetric::AVG) { - bucket.values.push_back(interval.value); - } else { - double sum = interval.value.type == LONG ? (double)interval.value.long_value - : interval.value.double_value; - bucket.values.push_back(Value((double)sum / interval.sampleSize)); - } - } - } - return bucket; -} - -void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) { - StatsdStats::getInstance().noteBucketCount(mMetricId); - // Cleanup data structure to aggregate values. - for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) { - bool obsolete = true; - for (auto& interval : it->second.intervals) { - interval.hasValue = false; - interval.sampleSize = 0; - if (interval.seenNewData) { - obsolete = false; - } - interval.seenNewData = false; - } - - if (obsolete && !mSlicedStateAtoms.empty()) { - // When slicing by state, only delete the MetricDimensionKey when the - // state key in the MetricDimensionKey is not the current state key. - const HashableDimensionKey& dimensionInWhatKey = it->first.getDimensionKeyInWhat(); - const auto& currentBaseInfoItr = mCurrentBaseInfo.find(dimensionInWhatKey); - - if ((currentBaseInfoItr != mCurrentBaseInfo.end()) && - (it->first.getStateValuesKey() == currentBaseInfoItr->second.currentState)) { - obsolete = false; - } - } - if (obsolete) { - it = mCurrentSlicedBucket.erase(it); - } else { - it++; - } - // TODO(b/157655103): remove mCurrentBaseInfo entries when obsolete - } - - mCurrentBucketIsSkipped = false; - mCurrentSkippedBucket.reset(); - - // If we do not have a global base when the condition is true, - // we will have incomplete bucket for the next bucket. - if (mUseDiff && !mHasGlobalBase && mCondition) { - mCurrentBucketIsSkipped = false; - } - mCurrentBucketStartTimeNs = nextBucketStartTimeNs; - VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, - (long long)mCurrentBucketStartTimeNs); -} - -void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) { - if (mCurrentBucketIsSkipped) { - if (isFullBucketReached) { - // If the bucket is invalid, we ignore the full bucket since it contains invalid data. - mCurrentFullBucket.clear(); - } - // Current bucket is invalid, we do not add it to the full bucket. - return; - } - - if (isFullBucketReached) { // If full bucket, send to anomaly tracker. - // Accumulate partial buckets with current value and then send to anomaly tracker. - if (mCurrentFullBucket.size() > 0) { - for (const auto& slice : mCurrentSlicedBucket) { - if (hitFullBucketGuardRailLocked(slice.first) || slice.second.intervals.empty()) { - continue; - } - // TODO: fix this when anomaly can accept double values - auto& interval = slice.second.intervals[0]; - if (interval.hasValue) { - mCurrentFullBucket[slice.first] += interval.value.long_value; - } - } - for (const auto& slice : mCurrentFullBucket) { - for (auto& tracker : mAnomalyTrackers) { - if (tracker != nullptr) { - tracker->addPastBucket(slice.first, slice.second, mCurrentBucketNum); - } - } - } - mCurrentFullBucket.clear(); - } else { - // Skip aggregating the partial buckets since there's no previous partial bucket. - for (const auto& slice : mCurrentSlicedBucket) { - for (auto& tracker : mAnomalyTrackers) { - if (tracker != nullptr && !slice.second.intervals.empty()) { - // TODO: fix this when anomaly can accept double values - auto& interval = slice.second.intervals[0]; - if (interval.hasValue) { - tracker->addPastBucket(slice.first, interval.value.long_value, - mCurrentBucketNum); - } - } - } - } - } - } else { - // Accumulate partial bucket. - for (const auto& slice : mCurrentSlicedBucket) { - if (!slice.second.intervals.empty()) { - // TODO: fix this when anomaly can accept double values - auto& interval = slice.second.intervals[0]; - if (interval.hasValue) { - mCurrentFullBucket[slice.first] += interval.value.long_value; - } - } - } - } -} - -size_t ValueMetricProducer::byteSizeLocked() const { - size_t totalSize = 0; - for (const auto& pair : mPastBuckets) { - totalSize += pair.second.size() * kBucketSize; - } - return totalSize; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h deleted file mode 100644 index ebd8fecd55d0..000000000000 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ /dev/null @@ -1,396 +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. - */ - -#pragma once - -#include <gtest/gtest_prod.h> -#include "anomaly/AnomalyTracker.h" -#include "condition/ConditionTimer.h" -#include "condition/ConditionTracker.h" -#include "external/PullDataReceiver.h" -#include "external/StatsPullerManager.h" -#include "matchers/EventMatcherWizard.h" -#include "stats_log_util.h" -#include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -namespace android { -namespace os { -namespace statsd { - -struct PastValueBucket { - int64_t mBucketStartNs; - int64_t mBucketEndNs; - std::vector<int> valueIndex; - std::vector<Value> values; - // If the metric has no condition, then this field is just wasted. - // When we tune statsd memory usage in the future, this is a candidate to optimize. - int64_t mConditionTrueNs; -}; - -// Aggregates values within buckets. -// -// There are different events that might complete a bucket -// - a condition change -// - an app upgrade -// - an alarm set to the end of the bucket -class ValueMetricProducer : public MetricProducer, public virtual PullDataReceiver { -public: - ValueMetricProducer( - const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, - const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash, - const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, - const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs, - const sp<StatsPullerManager>& pullerManager, - const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, - const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap = {}, - const vector<int>& slicedStateAtoms = {}, - const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {}); - - virtual ~ValueMetricProducer(); - - // Process data pulled on bucket boundary. - void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, - bool pullSuccess, int64_t originalPullTimeNs) override; - - // ValueMetric needs special logic if it's a pulled atom. - void notifyAppUpgrade(const int64_t& eventTimeNs) override { - std::lock_guard<std::mutex> lock(mMutex); - if (!mSplitBucketForAppUpgrade) { - return; - } - if (mIsPulled && mCondition == ConditionState::kTrue) { - pullAndMatchEventsLocked(eventTimeNs); - } - flushCurrentBucketLocked(eventTimeNs, eventTimeNs); - }; - - // ValueMetric needs special logic if it's a pulled atom. - void onStatsdInitCompleted(const int64_t& eventTimeNs) override { - std::lock_guard<std::mutex> lock(mMutex); - if (mIsPulled && mCondition == ConditionState::kTrue) { - pullAndMatchEventsLocked(eventTimeNs); - } - flushCurrentBucketLocked(eventTimeNs, eventTimeNs); - }; - - void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey, - const FieldValue& oldState, const FieldValue& newState) override; - - MetricType getMetricType() const override { - return METRIC_TYPE_VALUE; - } - -protected: - void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const std::map<int, HashableDimensionKey>& statePrimaryKeys) override; - -private: - void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - android::util::ProtoOutputStream* protoOutput) override; - void clearPastBucketsLocked(const int64_t dumpTimeNs) override; - - // Internal interface to handle active state change. - void onActiveStateChangedLocked(const int64_t& eventTimeNs) override; - - // Internal interface to handle condition change. - void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override; - - // Internal interface to handle sliced condition change. - void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - - // Internal function to calculate the current used bytes. - size_t byteSizeLocked() const override; - - void dumpStatesLocked(FILE* out, bool verbose) const override; - - // For pulled metrics, this method should only be called if a pull has be done. Else we will - // not have complete data for the bucket. - void flushIfNeededLocked(const int64_t& eventTime) override; - - // For pulled metrics, this method should only be called if a pulled have be done. Else we will - // not have complete data for the bucket. - void flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) override; - - void prepareFirstBucketLocked() override; - - void dropDataLocked(const int64_t dropTimeNs) override; - - // Calculate previous bucket end time based on current time. - int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs); - - // Calculate how many buckets are present between the current bucket and eventTimeNs. - int64_t calcBucketsForwardCount(const int64_t& eventTimeNs) const; - - // Mark the data as invalid. - void invalidateCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason); - - void invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs, - const BucketDropReason reason); - - // Skips the current bucket without notifying StatsdStats of the skipped bucket. - // This should only be called from #flushCurrentBucketLocked. Otherwise, a future event that - // causes the bucket to be invalidated will not notify StatsdStats. - void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason); - - bool onConfigUpdatedLocked( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const sp<ConditionWizard>& wizard, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation) override; - - int mWhatMatcherIndex; - - sp<EventMatcherWizard> mEventMatcherWizard; - - sp<StatsPullerManager> mPullerManager; - - // Value fields for matching. - std::vector<Matcher> mFieldMatchers; - - // Value fields for matching. - std::set<HashableDimensionKey> mMatchedMetricDimensionKeys; - - // Holds the atom id, primary key pair from a state change. - pair<int32_t, HashableDimensionKey> mStateChangePrimaryKey; - - // tagId for pulled data. -1 if this is not pulled - const int mPullTagId; - - // if this is pulled metric - const bool mIsPulled; - - // Tracks the value information of one value field. - typedef struct { - // Index in multi value aggregation. - int valueIndex; - // Current value, depending on the aggregation type. - Value value; - // Number of samples collected. - int sampleSize; - // If this dimension has any non-tainted value. If not, don't report the - // dimension. - bool hasValue = false; - // Whether new data is seen in the bucket. - bool seenNewData = false; - } Interval; - - // Internal state of an ongoing aggregation bucket. - typedef struct CurrentValueBucket { - // If the `MetricDimensionKey` state key is the current state key, then - // the condition timer will be updated later (e.g. condition/state/active - // state change) with the correct condition and time. - CurrentValueBucket() : intervals(), conditionTimer(ConditionTimer(false, 0)) {} - // Value information for each value field of the metric. - std::vector<Interval> intervals; - // Tracks how long the condition is true. - ConditionTimer conditionTimer; - } CurrentValueBucket; - - // Holds base information for diffing values from one value field. - typedef struct { - // Holds current base value of the dimension. Take diff and update if necessary. - Value base; - // Whether there is a base to diff to. - bool hasBase; - } BaseInfo; - - // State key and base information for a specific DimensionsInWhat key. - typedef struct DimensionsInWhatInfo { - DimensionsInWhatInfo(const HashableDimensionKey& stateKey) - : baseInfos(), currentState(stateKey), hasCurrentState(false) { - } - std::vector<BaseInfo> baseInfos; - // Last seen state value(s). - HashableDimensionKey currentState; - // Whether this dimensions in what key has a current state key. - bool hasCurrentState; - } DimensionsInWhatInfo; - - // Tracks the internal state in the ongoing aggregation bucket for each DimensionsInWhat - // key and StateValuesKey pair. - std::unordered_map<MetricDimensionKey, CurrentValueBucket> mCurrentSlicedBucket; - - // Tracks current state key and base information for each DimensionsInWhat key. - std::unordered_map<HashableDimensionKey, DimensionsInWhatInfo> mCurrentBaseInfo; - - std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket; - - // Save the past buckets and we can clear when the StatsLogReport is dumped. - std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>> mPastBuckets; - - const int64_t mMinBucketSizeNs; - - // Util function to check whether the specified dimension hits the guardrail. - bool hitGuardRailLocked(const MetricDimensionKey& newKey); - - bool hasReachedGuardRailLimit() const; - - bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey); - - void pullAndMatchEventsLocked(const int64_t timestampNs); - - bool multipleBucketsSkipped(const int64_t numBucketsForward); - - void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, - int64_t originalPullTimeNs, int64_t eventElapsedTimeNs); - - PastValueBucket buildPartialBucket(int64_t bucketEndTime, - const std::vector<Interval>& intervals); - - void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs); - - void appendToFullBucket(const bool isFullBucketReached); - - // Reset diff base and mHasGlobalBase - void resetBase(); - - // Updates the condition timers in the current sliced bucket when there is a - // condition change or an active state change. - void updateCurrentSlicedBucketConditionTimers(bool newCondition, int64_t eventTimeNs); - - static const size_t kBucketSize = sizeof(PastValueBucket{}); - - const size_t mDimensionSoftLimit; - - const size_t mDimensionHardLimit; - - const bool mUseAbsoluteValueOnReset; - - const ValueMetric::AggregationType mAggregationType; - - const bool mUseDiff; - - const ValueMetric::ValueDirection mValueDirection; - - const bool mSkipZeroDiffOutput; - - // If true, use a zero value as base to compute the diff. - // This is used for new keys which are present in the new data but was not - // present in the base data. - // The default base will only be used if we have a global base. - const bool mUseZeroDefaultBase; - - // For pulled metrics, this is always set to true whenever a pull succeeds. - // It is set to false when a pull fails, or upon condition change to false. - // This is used to decide if we have the right base data to compute the - // diff against. - bool mHasGlobalBase; - - // This is to track whether or not the bucket is skipped for any of the reasons listed in - // BucketDropReason, many of which make the bucket potentially invalid. - bool mCurrentBucketIsSkipped; - - const int64_t mMaxPullDelayNs; - - const bool mSplitBucketForAppUpgrade; - - ConditionTimer mConditionTimer; - - FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); - FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2); - FRIEND_TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet); - FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime); - FRIEND_TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged); - FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary); - FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged); - FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled); - FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition); - FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket); - FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff); - FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff); - FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries); - FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse); - FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue); - FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure); - FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges); - FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset); - FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset); - FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering); - FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled); - FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg); - FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax); - FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin); - FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum); - FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate); - FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput); - FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedState); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMap); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithCondition); - FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey); - FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase); - FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary); - - FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed); - FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed); - FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed); - FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit); - FRIEND_TEST(ValueMetricProducerTest_BucketDrop, - TestInvalidBucketWhenAccumulateEventWrongBucket); - - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket); - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid); - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated); - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPushedEvents); - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValue); - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse); - - FRIEND_TEST(ConfigUpdateTest, TestUpdateValueMetrics); - - friend class ValueMetricProducerTestHelper; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h deleted file mode 100644 index cf1f437c4168..000000000000 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ /dev/null @@ -1,240 +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. - */ - -#ifndef DURATION_TRACKER_H -#define DURATION_TRACKER_H - -#include "anomaly/DurationAnomalyTracker.h" -#include "condition/ConditionWizard.h" -#include "config/ConfigKey.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -enum DurationState { - kStopped = 0, // The event is stopped. - kStarted = 1, // The event is on going. - kPaused = 2, // The event is started, but condition is false, clock is paused. When condition - // turns to true, kPaused will become kStarted. -}; - -// Hold duration information for one atom level duration in current on-going bucket. -struct DurationInfo { - DurationState state; - - // the number of starts seen. - int32_t startCount; - - // most recent start time. - int64_t lastStartTime; - // existing duration in current bucket. - int64_t lastDuration; - // cache the HashableDimensionKeys we need to query the condition for this duration event. - ConditionKey conditionKeys; - - DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){}; -}; - -struct DurationBucket { - int64_t mBucketStartNs; - int64_t mBucketEndNs; - int64_t mDuration; -}; - -struct DurationValues { - // Recorded duration for current partial bucket. - int64_t mDuration; - - // Sum of past partial bucket durations in current full bucket. - // Used for anomaly detection. - int64_t mDurationFullBucket; -}; - -class DurationTracker { -public: - DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, - sp<ConditionWizard> wizard, int conditionIndex, bool nesting, - int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs, - int64_t bucketSizeNs, bool conditionSliced, bool fullLink, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers) - : mConfigKey(key), - mTrackerId(id), - mEventKey(eventKey), - mWizard(wizard), - mConditionTrackerIndex(conditionIndex), - mBucketSizeNs(bucketSizeNs), - mNested(nesting), - mCurrentBucketStartTimeNs(currentBucketStartNs), - mDuration(0), - mCurrentBucketNum(currentBucketNum), - mStartTimeNs(startTimeNs), - mConditionSliced(conditionSliced), - mHasLinksToAllConditionDimensionsInTracker(fullLink), - mAnomalyTrackers(anomalyTrackers){}; - - virtual ~DurationTracker(){}; - - void onConfigUpdated(const sp<ConditionWizard>& wizard, const int conditionTrackerIndex) { - sp<ConditionWizard> tmpWizard = mWizard; - mWizard = wizard; - mConditionTrackerIndex = conditionTrackerIndex; - mAnomalyTrackers.clear(); - }; - - virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime, - const ConditionKey& conditionKey) = 0; - virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime, - const bool stopAll) = 0; - virtual void noteStopAll(const int64_t eventTime) = 0; - - virtual void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) = 0; - virtual void onConditionChanged(bool condition, const int64_t timestamp) = 0; - - virtual void onStateChanged(const int64_t timestamp, const int32_t atomId, - const FieldValue& newState) = 0; - - // Flush stale buckets if needed, and return true if the tracker has no on-going duration - // events, so that the owner can safely remove the tracker. - virtual bool flushIfNeeded( - int64_t timestampNs, - std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0; - - // Should only be called during an app upgrade or from this tracker's flushIfNeeded. If from - // an app upgrade, we assume that we're trying to form a partial bucket. - virtual bool flushCurrentBucket( - const int64_t& eventTimeNs, - std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0; - - // Predict the anomaly timestamp given the current status. - virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker, - const int64_t currentTimestamp) const = 0; - // Dump internal states for debugging - virtual void dumpStates(FILE* out, bool verbose) const = 0; - - virtual int64_t getCurrentStateKeyDuration() const = 0; - - virtual int64_t getCurrentStateKeyFullBucketDuration() const = 0; - - // Replace old value with new value for the given state atom. - virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0; - - void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) { - mAnomalyTrackers.push_back(anomalyTracker); - } - -protected: - int64_t getCurrentBucketEndTimeNs() const { - return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs; - } - - // Starts the anomaly alarm. - void startAnomalyAlarm(const int64_t eventTime) { - for (auto& anomalyTracker : mAnomalyTrackers) { - if (anomalyTracker != nullptr) { - const int64_t alarmTimestampNs = - predictAnomalyTimestampNs(*anomalyTracker, eventTime); - if (alarmTimestampNs > 0) { - anomalyTracker->startAlarm(mEventKey, alarmTimestampNs); - } - } - } - } - - // Stops the anomaly alarm. If it should have already fired, declare the anomaly now. - void stopAnomalyAlarm(const int64_t timestamp) { - for (auto& anomalyTracker : mAnomalyTrackers) { - if (anomalyTracker != nullptr) { - anomalyTracker->stopAlarm(mEventKey, timestamp); - } - } - } - - void addPastBucketToAnomalyTrackers(const MetricDimensionKey eventKey, - const int64_t& bucketValue, const int64_t& bucketNum) { - for (auto& anomalyTracker : mAnomalyTrackers) { - if (anomalyTracker != nullptr) { - anomalyTracker->addPastBucket(eventKey, bucketValue, bucketNum); - } - } - } - - void detectAndDeclareAnomaly(const int64_t& timestamp, const int64_t& currBucketNum, - const int64_t& currentBucketValue) { - for (auto& anomalyTracker : mAnomalyTrackers) { - if (anomalyTracker != nullptr) { - anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId, - mEventKey, currentBucketValue); - } - } - } - - // Convenience to compute the current bucket's end time, which is always aligned with the - // start time of the metric. - int64_t getCurrentBucketEndTimeNs() { - return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs; - } - - void setEventKey(const MetricDimensionKey& eventKey) { - mEventKey = eventKey; - } - - // A reference to the DurationMetricProducer's config key. - const ConfigKey& mConfigKey; - - const int64_t mTrackerId; - - MetricDimensionKey mEventKey; - - sp<ConditionWizard> mWizard; - - int mConditionTrackerIndex; - - const int64_t mBucketSizeNs; - - const bool mNested; - - int64_t mCurrentBucketStartTimeNs; - - int64_t mDuration; // current recorded duration result (for partial bucket) - - // Recorded duration results for each state key in the current partial bucket. - std::unordered_map<HashableDimensionKey, DurationValues> mStateKeyDurationMap; - - int64_t mCurrentBucketNum; - - const int64_t mStartTimeNs; - - const bool mConditionSliced; - - bool mHasLinksToAllConditionDimensionsInTracker; - - std::vector<sp<AnomalyTracker>> mAnomalyTrackers; - - FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm); - - FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics); - FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // DURATION_TRACKER_H diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp deleted file mode 100644 index 62f49824b874..000000000000 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ /dev/null @@ -1,331 +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. - */ - -#define DEBUG false - -#include "Log.h" -#include "MaxDurationTracker.h" -#include "guardrail/StatsdStats.h" - -namespace android { -namespace os { -namespace statsd { - -MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id, - const MetricDimensionKey& eventKey, - sp<ConditionWizard> wizard, int conditionIndex, bool nesting, - int64_t currentBucketStartNs, int64_t currentBucketNum, - int64_t startTimeNs, int64_t bucketSizeNs, - bool conditionSliced, bool fullLink, - const vector<sp<AnomalyTracker>>& anomalyTrackers) - : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink, - anomalyTrackers) { -} - -bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { - // ===========GuardRail============== - if (mInfos.find(newKey) != mInfos.end()) { - // if the key existed, we are good! - return false; - } - // 1. Report the tuple count if the tuple count > soft limit - if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mInfos.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("MaxDurTracker %lld dropping data for dimension key %s", - (long long)mTrackerId, newKey.toString().c_str()); - return true; - } - } - return false; -} - -void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition, - const int64_t eventTime, const ConditionKey& conditionKey) { - // this will construct a new DurationInfo if this key didn't exist. - if (hitGuardRail(key)) { - return; - } - - DurationInfo& duration = mInfos[key]; - if (mConditionSliced) { - duration.conditionKeys = conditionKey; - } - VLOG("MaxDuration: key %s start condition %d", key.toString().c_str(), condition); - - switch (duration.state) { - case kStarted: - duration.startCount++; - break; - case kPaused: - duration.startCount++; - break; - case kStopped: - if (!condition) { - // event started, but we need to wait for the condition to become true. - duration.state = DurationState::kPaused; - } else { - duration.state = DurationState::kStarted; - duration.lastStartTime = eventTime; - startAnomalyAlarm(eventTime); - } - duration.startCount = 1; - break; - } -} - -void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const int64_t eventTime, - bool forceStop) { - VLOG("MaxDuration: key %s stop", key.toString().c_str()); - if (mInfos.find(key) == mInfos.end()) { - // we didn't see a start event before. do nothing. - return; - } - DurationInfo& duration = mInfos[key]; - - switch (duration.state) { - case DurationState::kStopped: - // already stopped, do nothing. - break; - case DurationState::kStarted: { - duration.startCount--; - if (forceStop || !mNested || duration.startCount <= 0) { - stopAnomalyAlarm(eventTime); - duration.state = DurationState::kStopped; - int64_t durationTime = eventTime - duration.lastStartTime; - VLOG("Max, key %s, Stop %lld %lld %lld", key.toString().c_str(), - (long long)duration.lastStartTime, (long long)eventTime, - (long long)durationTime); - duration.lastDuration += durationTime; - if (anyStarted()) { - // In case any other dimensions are still started, we need to keep the alarm - // set. - startAnomalyAlarm(eventTime); - } - VLOG(" record duration: %lld ", (long long)duration.lastDuration); - } - break; - } - case DurationState::kPaused: { - duration.startCount--; - if (forceStop || !mNested || duration.startCount <= 0) { - duration.state = DurationState::kStopped; - } - break; - } - } - - if (duration.lastDuration > mDuration) { - mDuration = duration.lastDuration; - VLOG("Max: new max duration: %lld", (long long)mDuration); - } - // Once an atom duration ends, we erase it. Next time, if we see another atom event with the - // same name, they are still considered as different atom durations. - if (duration.state == DurationState::kStopped) { - mInfos.erase(key); - } -} - -bool MaxDurationTracker::anyStarted() { - for (auto& pair : mInfos) { - if (pair.second.state == kStarted) { - return true; - } - } - return false; -} - -void MaxDurationTracker::noteStopAll(const int64_t eventTime) { - std::set<HashableDimensionKey> keys; - for (const auto& pair : mInfos) { - keys.insert(pair.first); - } - for (auto& key : keys) { - noteStop(key, eventTime, true); - } -} - -bool MaxDurationTracker::flushCurrentBucket( - const int64_t& eventTimeNs, - std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) { - VLOG("MaxDurationTracker flushing....."); - - // adjust the bucket start time - int numBucketsForward = 0; - int64_t fullBucketEnd = getCurrentBucketEndTimeNs(); - int64_t currentBucketEndTimeNs; - if (eventTimeNs >= fullBucketEnd) { - numBucketsForward = 1 + (eventTimeNs - fullBucketEnd) / mBucketSizeNs; - currentBucketEndTimeNs = fullBucketEnd; - } else { - // This must be a partial bucket. - currentBucketEndTimeNs = eventTimeNs; - } - - bool hasPendingEvent = - false; // has either a kStarted or kPaused event across bucket boundaries - // meaning we need to carry them over to the new bucket. - for (auto it = mInfos.begin(); it != mInfos.end();) { - if (it->second.state == DurationState::kStopped) { - // No need to keep buckets for events that were stopped before. - it = mInfos.erase(it); - } else { - ++it; - hasPendingEvent = true; - } - } - - // mDuration is updated in noteStop to the maximum duration that ended in the current bucket. - if (mDuration != 0) { - DurationBucket info; - info.mBucketStartNs = mCurrentBucketStartTimeNs; - info.mBucketEndNs = currentBucketEndTimeNs; - info.mDuration = mDuration; - (*output)[mEventKey].push_back(info); - VLOG(" final duration for last bucket: %lld", (long long)mDuration); - } - - if (numBucketsForward > 0) { - mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs; - mCurrentBucketNum += numBucketsForward; - } else { // We must be forming a partial bucket. - mCurrentBucketStartTimeNs = eventTimeNs; - } - - mDuration = 0; - // If this tracker has no pending events, tell owner to remove. - return !hasPendingEvent; -} - -bool MaxDurationTracker::flushIfNeeded( - int64_t eventTimeNs, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) { - if (eventTimeNs < getCurrentBucketEndTimeNs()) { - return false; - } - return flushCurrentBucket(eventTimeNs, output); -} - -void MaxDurationTracker::onSlicedConditionMayChange(bool overallCondition, - const int64_t timestamp) { - // Now for each of the on-going event, check if the condition has changed for them. - for (auto& pair : mInfos) { - if (pair.second.state == kStopped) { - continue; - } - ConditionState conditionState = mWizard->query( - mConditionTrackerIndex, pair.second.conditionKeys, - !mHasLinksToAllConditionDimensionsInTracker); - bool conditionMet = (conditionState == ConditionState::kTrue); - - VLOG("key: %s, condition: %d", pair.first.toString().c_str(), conditionMet); - noteConditionChanged(pair.first, conditionMet, timestamp); - } -} - -void MaxDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId, - const FieldValue& newState) { - ALOGE("MaxDurationTracker does not handle sliced state changes."); -} - -void MaxDurationTracker::onConditionChanged(bool condition, const int64_t timestamp) { - for (auto& pair : mInfos) { - noteConditionChanged(pair.first, condition, timestamp); - } -} - -void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet, - const int64_t timestamp) { - auto it = mInfos.find(key); - if (it == mInfos.end()) { - return; - } - - switch (it->second.state) { - case kStarted: - // If condition becomes false, kStarted -> kPaused. Record the current duration and - // stop anomaly alarm. - if (!conditionMet) { - stopAnomalyAlarm(timestamp); - it->second.state = DurationState::kPaused; - it->second.lastDuration += (timestamp - it->second.lastStartTime); - if (anyStarted()) { - // In case any other dimensions are still started, we need to set the alarm. - startAnomalyAlarm(timestamp); - } - VLOG("MaxDurationTracker Key: %s Started->Paused ", key.toString().c_str()); - } - break; - case kStopped: - // Nothing to do if it's stopped. - break; - case kPaused: - // If condition becomes true, kPaused -> kStarted. and the start time is the condition - // change time. - if (conditionMet) { - it->second.state = DurationState::kStarted; - it->second.lastStartTime = timestamp; - startAnomalyAlarm(timestamp); - VLOG("MaxDurationTracker Key: %s Paused->Started", key.toString().c_str()); - } - break; - } - // Note that we don't update mDuration here since it's only updated during noteStop. -} - -int64_t MaxDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker, - const int64_t currentTimestamp) const { - // The allowed time we can continue in the current state is the - // (anomaly threshold) - max(elapsed time of the started mInfos). - int64_t maxElapsed = 0; - for (auto it = mInfos.begin(); it != mInfos.end(); ++it) { - if (it->second.state == DurationState::kStarted) { - int64_t duration = - it->second.lastDuration + (currentTimestamp - it->second.lastStartTime); - if (duration > maxElapsed) { - maxElapsed = duration; - } - } - } - int64_t anomalyTimeNs = currentTimestamp + anomalyTracker.getAnomalyThreshold() - maxElapsed; - int64_t refractoryEndNs = anomalyTracker.getRefractoryPeriodEndsSec(mEventKey) * NS_PER_SEC; - return std::max(anomalyTimeNs, refractoryEndNs); -} - -void MaxDurationTracker::dumpStates(FILE* out, bool verbose) const { - fprintf(out, "\t\t sub-durations %lu\n", (unsigned long)mInfos.size()); - fprintf(out, "\t\t current duration %lld\n", (long long)mDuration); -} - -int64_t MaxDurationTracker::getCurrentStateKeyDuration() const { - ALOGE("MaxDurationTracker does not handle sliced state changes."); - return -1; -} - -int64_t MaxDurationTracker::getCurrentStateKeyFullBucketDuration() const { - ALOGE("MaxDurationTracker does not handle sliced state changes."); - return -1; -} - -void MaxDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) { - ALOGE("MaxDurationTracker does not handle sliced state changes."); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h deleted file mode 100644 index be2707c60c1b..000000000000 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ /dev/null @@ -1,92 +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. - */ - -#ifndef MAX_DURATION_TRACKER_H -#define MAX_DURATION_TRACKER_H - -#include "DurationTracker.h" - -namespace android { -namespace os { -namespace statsd { - -// Tracks a pool of atom durations, and output the max duration for each bucket. -// To get max duration, we need to keep track of each individual durations, and compare them when -// they stop or bucket expires. -class MaxDurationTracker : public DurationTracker { -public: - MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, - sp<ConditionWizard> wizard, int conditionIndex, bool nesting, - int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs, - int64_t bucketSizeNs, bool conditionSliced, bool fullLink, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers); - - MaxDurationTracker(const MaxDurationTracker& tracker) = default; - - void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime, - const ConditionKey& conditionKey) override; - void noteStop(const HashableDimensionKey& key, const int64_t eventTime, - const bool stopAll) override; - void noteStopAll(const int64_t eventTime) override; - - bool flushIfNeeded( - int64_t timestampNs, - std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override; - bool flushCurrentBucket( - const int64_t& eventTimeNs, - std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>*) override; - - void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override; - void onConditionChanged(bool condition, const int64_t timestamp) override; - - void onStateChanged(const int64_t timestamp, const int32_t atomId, - const FieldValue& newState) override; - - int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker, - const int64_t currentTimestamp) const override; - void dumpStates(FILE* out, bool verbose) const override; - - int64_t getCurrentStateKeyDuration() const override; - - int64_t getCurrentStateKeyFullBucketDuration() const override; - - void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState); - -private: - // Returns true if at least one of the mInfos is started. - bool anyStarted(); - - std::unordered_map<HashableDimensionKey, DurationInfo> mInfos; - - void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet, - const int64_t timestamp); - - // return true if we should not allow newKey to be tracked because we are above the threshold - bool hitGuardRail(const HashableDimensionKey& newKey); - - FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration); - FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary); - FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition); - FRIEND_TEST(MaxDurationTrackerTest, TestStopAll); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // MAX_DURATION_TRACKER_H diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp deleted file mode 100644 index 247e2e01c992..000000000000 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ /dev/null @@ -1,462 +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. - */ -#define DEBUG false -#include "Log.h" -#include "OringDurationTracker.h" -#include "guardrail/StatsdStats.h" - -namespace android { -namespace os { -namespace statsd { - -using std::pair; - -OringDurationTracker::OringDurationTracker( - const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, - sp<ConditionWizard> wizard, int conditionIndex, bool nesting, int64_t currentBucketStartNs, - int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced, - bool fullLink, const vector<sp<AnomalyTracker>>& anomalyTrackers) - : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink, - anomalyTrackers), - mStarted(), - mPaused() { - mLastStartTime = 0; -} - -bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { - // ===========GuardRail============== - // 1. Report the tuple count if the tuple count > soft limit - if (mConditionKeyMap.find(newKey) != mConditionKeyMap.end()) { - return false; - } - if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mConditionKeyMap.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("OringDurTracker %lld dropping data for dimension key %s", - (long long)mTrackerId, newKey.toString().c_str()); - return true; - } - } - return false; -} - -void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition, - const int64_t eventTime, const ConditionKey& conditionKey) { - if (hitGuardRail(key)) { - return; - } - if (condition) { - if (mStarted.size() == 0) { - mLastStartTime = eventTime; - VLOG("record first start...."); - startAnomalyAlarm(eventTime); - } - mStarted[key]++; - } else { - mPaused[key]++; - } - - if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) { - mConditionKeyMap[key] = conditionKey; - } - VLOG("Oring: %s start, condition %d", key.toString().c_str(), condition); -} - -void OringDurationTracker::noteStop(const HashableDimensionKey& key, const int64_t timestamp, - const bool stopAll) { - VLOG("Oring: %s stop", key.toString().c_str()); - auto it = mStarted.find(key); - if (it != mStarted.end()) { - (it->second)--; - if (stopAll || !mNested || it->second <= 0) { - mStarted.erase(it); - mConditionKeyMap.erase(key); - } - if (mStarted.empty()) { - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += - (timestamp - mLastStartTime); - detectAndDeclareAnomaly( - timestamp, mCurrentBucketNum, - getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration()); - VLOG("record duration %lld, total duration %lld for state key %s", - (long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(), - mEventKey.getStateValuesKey().toString().c_str()); - } - } - - auto pausedIt = mPaused.find(key); - if (pausedIt != mPaused.end()) { - (pausedIt->second)--; - if (stopAll || !mNested || pausedIt->second <= 0) { - mPaused.erase(pausedIt); - mConditionKeyMap.erase(key); - } - } - if (mStarted.empty()) { - stopAnomalyAlarm(timestamp); - } -} - -void OringDurationTracker::noteStopAll(const int64_t timestamp) { - if (!mStarted.empty()) { - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += - (timestamp - mLastStartTime); - VLOG("Oring Stop all: record duration %lld, total duration %lld for state key %s", - (long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(), - mEventKey.getStateValuesKey().toString().c_str()); - detectAndDeclareAnomaly( - timestamp, mCurrentBucketNum, - getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration()); - } - - stopAnomalyAlarm(timestamp); - mStarted.clear(); - mPaused.clear(); - mConditionKeyMap.clear(); -} - -bool OringDurationTracker::flushCurrentBucket( - const int64_t& eventTimeNs, - std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) { - VLOG("OringDurationTracker Flushing............."); - - // Note that we have to mimic the bucket time changes we do in the - // MetricProducer#notifyAppUpgrade. - - int numBucketsForward = 0; - int64_t fullBucketEnd = getCurrentBucketEndTimeNs(); - int64_t currentBucketEndTimeNs; - - if (eventTimeNs >= fullBucketEnd) { - numBucketsForward = 1 + (eventTimeNs - fullBucketEnd) / mBucketSizeNs; - currentBucketEndTimeNs = fullBucketEnd; - } else { - // This must be a partial bucket. - currentBucketEndTimeNs = eventTimeNs; - } - - // Process the current bucket. - if (mStarted.size() > 0) { - // Calculate the duration for the current state key. - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += - (currentBucketEndTimeNs - mLastStartTime); - } - // Store DurationBucket info for each whatKey, stateKey pair. - // Note: The whatKey stored in mEventKey is constant for each DurationTracker, while the - // stateKey stored in mEventKey is only the current stateKey. mStateKeyDurationMap is used to - // store durations for each stateKey, so we need to flush the bucket by creating a - // DurationBucket for each stateKey. - for (auto& durationIt : mStateKeyDurationMap) { - if (durationIt.second.mDuration > 0) { - DurationBucket current_info; - current_info.mBucketStartNs = mCurrentBucketStartTimeNs; - current_info.mBucketEndNs = currentBucketEndTimeNs; - current_info.mDuration = durationIt.second.mDuration; - (*output)[MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first)] - .push_back(current_info); - - durationIt.second.mDurationFullBucket += durationIt.second.mDuration; - VLOG(" duration: %lld", (long long)current_info.mDuration); - } - - if (eventTimeNs > fullBucketEnd) { - // End of full bucket, can send to anomaly tracker now. - addPastBucketToAnomalyTrackers( - MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first), - getCurrentStateKeyFullBucketDuration(), mCurrentBucketNum); - durationIt.second.mDurationFullBucket = 0; - } - durationIt.second.mDuration = 0; - } - - if (mStarted.size() > 0) { - for (int i = 1; i < numBucketsForward; i++) { - DurationBucket info; - info.mBucketStartNs = fullBucketEnd + mBucketSizeNs * (i - 1); - info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs; - info.mDuration = mBucketSizeNs; - // Full duration buckets are attributed to the current stateKey. - (*output)[mEventKey].push_back(info); - // Safe to send these buckets to anomaly tracker since they must be full buckets. - // If it's a partial bucket, numBucketsForward would be 0. - addPastBucketToAnomalyTrackers(mEventKey, info.mDuration, mCurrentBucketNum + i); - VLOG(" add filling bucket with duration %lld", (long long)info.mDuration); - } - } else { - if (numBucketsForward >= 2) { - addPastBucketToAnomalyTrackers(mEventKey, 0, mCurrentBucketNum + numBucketsForward - 1); - } - } - - if (numBucketsForward > 0) { - mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs; - mCurrentBucketNum += numBucketsForward; - } else { // We must be forming a partial bucket. - mCurrentBucketStartTimeNs = eventTimeNs; - } - mLastStartTime = mCurrentBucketStartTimeNs; - - // if all stopped, then tell owner it's safe to remove this tracker. - return mStarted.empty() && mPaused.empty(); -} - -bool OringDurationTracker::flushIfNeeded( - int64_t eventTimeNs, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) { - if (eventTimeNs < getCurrentBucketEndTimeNs()) { - return false; - } - return flushCurrentBucket(eventTimeNs, output); -} - -void OringDurationTracker::onSlicedConditionMayChange(bool overallCondition, - const int64_t timestamp) { - vector<pair<HashableDimensionKey, int>> startedToPaused; - vector<pair<HashableDimensionKey, int>> pausedToStarted; - if (!mStarted.empty()) { - for (auto it = mStarted.begin(); it != mStarted.end();) { - const auto& key = it->first; - const auto& condIt = mConditionKeyMap.find(key); - if (condIt == mConditionKeyMap.end()) { - VLOG("Key %s dont have condition key", key.toString().c_str()); - ++it; - continue; - } - ConditionState conditionState = - mWizard->query(mConditionTrackerIndex, condIt->second, - !mHasLinksToAllConditionDimensionsInTracker); - if (conditionState != ConditionState::kTrue) { - startedToPaused.push_back(*it); - it = mStarted.erase(it); - VLOG("Key %s started -> paused", key.toString().c_str()); - } else { - ++it; - } - } - - if (mStarted.empty()) { - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += - (timestamp - mLastStartTime); - VLOG("record duration %lld, total duration %lld for state key %s", - (long long)(timestamp - mLastStartTime), (long long)getCurrentStateKeyDuration(), - mEventKey.getStateValuesKey().toString().c_str()); - detectAndDeclareAnomaly( - timestamp, mCurrentBucketNum, - getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration()); - } - } - - if (!mPaused.empty()) { - for (auto it = mPaused.begin(); it != mPaused.end();) { - const auto& key = it->first; - if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) { - VLOG("Key %s dont have condition key", key.toString().c_str()); - ++it; - continue; - } - ConditionState conditionState = - mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key], - !mHasLinksToAllConditionDimensionsInTracker); - if (conditionState == ConditionState::kTrue) { - pausedToStarted.push_back(*it); - it = mPaused.erase(it); - VLOG("Key %s paused -> started", key.toString().c_str()); - } else { - ++it; - } - } - - if (mStarted.empty() && pausedToStarted.size() > 0) { - mLastStartTime = timestamp; - } - } - - if (mStarted.empty() && !pausedToStarted.empty()) { - startAnomalyAlarm(timestamp); - } - mStarted.insert(pausedToStarted.begin(), pausedToStarted.end()); - mPaused.insert(startedToPaused.begin(), startedToPaused.end()); - - if (mStarted.empty()) { - stopAnomalyAlarm(timestamp); - } -} - -void OringDurationTracker::onConditionChanged(bool condition, const int64_t timestamp) { - if (condition) { - if (!mPaused.empty()) { - VLOG("Condition true, all started"); - if (mStarted.empty()) { - mLastStartTime = timestamp; - } - if (mStarted.empty() && !mPaused.empty()) { - startAnomalyAlarm(timestamp); - } - mStarted.insert(mPaused.begin(), mPaused.end()); - mPaused.clear(); - } - } else { - if (!mStarted.empty()) { - VLOG("Condition false, all paused"); - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += - (timestamp - mLastStartTime); - mPaused.insert(mStarted.begin(), mStarted.end()); - mStarted.clear(); - detectAndDeclareAnomaly( - timestamp, mCurrentBucketNum, - getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration()); - } - } - if (mStarted.empty()) { - stopAnomalyAlarm(timestamp); - } -} - -void OringDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId, - const FieldValue& newState) { - // Nothing needs to be done on a state change if we have not seen a start - // event, the metric is currently not active, or condition is false. - // For these cases, no keys are being tracked in mStarted, so update - // the current state key and return. - if (mStarted.empty()) { - updateCurrentStateKey(atomId, newState); - return; - } - // Add the current duration length to the previous state key and then update - // the last start time and current state key. - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += (timestamp - mLastStartTime); - mLastStartTime = timestamp; - updateCurrentStateKey(atomId, newState); -} - -int64_t OringDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker, - const int64_t eventTimestampNs) const { - // The anomaly threshold. - const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold(); - - // The timestamp of the current bucket end. - const int64_t currentBucketEndNs = getCurrentBucketEndTimeNs(); - - // The past duration ns for the current bucket of the current stateKey. - int64_t currentStateBucketPastNs = - getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration(); - - // As we move into the future, old buckets get overwritten (so their old data is erased). - // Sum of past durations. Will change as we overwrite old buckets. - int64_t pastNs = currentStateBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey); - - // The refractory period end timestamp for dimension mEventKey. - const int64_t refractoryPeriodEndNs = - anomalyTracker.getRefractoryPeriodEndsSec(mEventKey) * NS_PER_SEC; - - // The anomaly should happen when accumulated wakelock duration is above the threshold and - // not within the refractory period. - int64_t anomalyTimestampNs = - std::max(eventTimestampNs + thresholdNs - pastNs, refractoryPeriodEndNs); - // If the predicted the anomaly timestamp is within the current bucket, return it directly. - if (anomalyTimestampNs <= currentBucketEndNs) { - return std::max(eventTimestampNs, anomalyTimestampNs); - } - - // Remove the old bucket. - if (anomalyTracker.getNumOfPastBuckets() > 0) { - pastNs -= anomalyTracker.getPastBucketValue( - mEventKey, - mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets()); - // Add the remaining of the current bucket to the accumulated wakelock duration. - pastNs += (currentBucketEndNs - eventTimestampNs); - } else { - // The anomaly depends on only one bucket. - pastNs = 0; - } - - // The anomaly will not happen in the current bucket. We need to iterate over the future buckets - // to predict the accumulated wakelock duration and determine the anomaly timestamp accordingly. - for (int futureBucketIdx = 1; futureBucketIdx <= anomalyTracker.getNumOfPastBuckets() + 1; - futureBucketIdx++) { - // The alarm candidate timestamp should meet two requirements: - // 1. the accumulated wakelock duration is above the threshold. - // 2. it is not within the refractory period. - // 3. the alarm timestamp falls in this bucket. Otherwise we need to flush the past buckets, - // find the new alarm candidate timestamp and check these requirements again. - const int64_t bucketEndNs = currentBucketEndNs + futureBucketIdx * mBucketSizeNs; - int64_t anomalyTimestampNs = - std::max(bucketEndNs - mBucketSizeNs + thresholdNs - pastNs, refractoryPeriodEndNs); - if (anomalyTimestampNs <= bucketEndNs) { - return anomalyTimestampNs; - } - if (anomalyTracker.getNumOfPastBuckets() <= 0) { - continue; - } - - // No valid alarm timestamp is found in this bucket. The clock moves to the end of the - // bucket. Update the pastNs. - pastNs += mBucketSizeNs; - // 1. If the oldest past bucket is still in the past bucket window, we could fetch the past - // bucket and erase it from pastNs. - // 2. If the oldest past bucket is the current bucket, we should compute the - // wakelock duration in the current bucket and erase it from pastNs. - // 3. Otherwise all othe past buckets are ancient. - if (futureBucketIdx < anomalyTracker.getNumOfPastBuckets()) { - pastNs -= anomalyTracker.getPastBucketValue( - mEventKey, - mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futureBucketIdx); - } else if (futureBucketIdx == anomalyTracker.getNumOfPastBuckets()) { - pastNs -= (currentStateBucketPastNs + (currentBucketEndNs - eventTimestampNs)); - } - } - - return std::max(eventTimestampNs + thresholdNs, refractoryPeriodEndNs); -} - -void OringDurationTracker::dumpStates(FILE* out, bool verbose) const { - fprintf(out, "\t\t started count %lu\n", (unsigned long)mStarted.size()); - fprintf(out, "\t\t paused count %lu\n", (unsigned long)mPaused.size()); - fprintf(out, "\t\t current duration %lld\n", (long long)getCurrentStateKeyDuration()); -} - -int64_t OringDurationTracker::getCurrentStateKeyDuration() const { - auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey()); - if (it == mStateKeyDurationMap.end()) { - return 0; - } else { - return it->second.mDuration; - } -} - -int64_t OringDurationTracker::getCurrentStateKeyFullBucketDuration() const { - auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey()); - if (it == mStateKeyDurationMap.end()) { - return 0; - } else { - return it->second.mDurationFullBucket; - } -} - -void OringDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) { - HashableDimensionKey* stateValuesKey = mEventKey.getMutableStateValuesKey(); - for (size_t i = 0; i < stateValuesKey->getValues().size(); i++) { - if (stateValuesKey->getValues()[i].mField.getTag() == atomId) { - stateValuesKey->mutableValue(i)->mValue = newState.mValue; - } - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h deleted file mode 100644 index 6eddee7da252..000000000000 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ /dev/null @@ -1,93 +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. - */ - -#ifndef ORING_DURATION_TRACKER_H -#define ORING_DURATION_TRACKER_H - -#include "DurationTracker.h" - -namespace android { -namespace os { -namespace statsd { - -// Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted. -class OringDurationTracker : public DurationTracker { -public: - OringDurationTracker(const ConfigKey& key, const int64_t& id, - const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard, - int conditionIndex, bool nesting, int64_t currentBucketStartNs, - int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, - bool conditionSliced, bool fullLink, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers); - - OringDurationTracker(const OringDurationTracker& tracker) = default; - - void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime, - const ConditionKey& conditionKey) override; - void noteStop(const HashableDimensionKey& key, const int64_t eventTime, - const bool stopAll) override; - void noteStopAll(const int64_t eventTime) override; - - void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override; - void onConditionChanged(bool condition, const int64_t timestamp) override; - - void onStateChanged(const int64_t timestamp, const int32_t atomId, - const FieldValue& newState) override; - - bool flushCurrentBucket( - const int64_t& eventTimeNs, - std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override; - bool flushIfNeeded( - int64_t timestampNs, - std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override; - - int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker, - const int64_t currentTimestamp) const override; - void dumpStates(FILE* out, bool verbose) const override; - - int64_t getCurrentStateKeyDuration() const override; - - int64_t getCurrentStateKeyFullBucketDuration() const override; - - void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState); - -private: - // We don't need to keep track of individual durations. The information that's needed is: - // 1) which keys are started. We record the first start time. - // 2) which keys are paused (started but condition was false) - // 3) whenever a key stops, we remove it from the started set. And if the set becomes empty, - // it means everything has stopped, we then record the end time. - std::unordered_map<HashableDimensionKey, int> mStarted; - std::unordered_map<HashableDimensionKey, int> mPaused; - int64_t mLastStartTime; - std::unordered_map<HashableDimensionKey, ConditionKey> mConditionKeyMap; - - // return true if we should not allow newKey to be tracked because we are above the threshold - bool hitGuardRail(const HashableDimensionKey& newKey); - - FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap); - FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary); - FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange); - FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // ORING_DURATION_TRACKER_H diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp deleted file mode 100644 index 39789cd86bb1..000000000000 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp +++ /dev/null @@ -1,1118 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "config_update_utils.h" - -#include "external/StatsPullerManager.h" -#include "hash.h" -#include "matchers/EventMatcherWizard.h" -#include "metrics_manager_util.h" - -using google::protobuf::MessageLite; - -namespace android { -namespace os { -namespace statsd { - -// Recursive function to determine if a matcher needs to be updated. Populates matcherToUpdate. -// Returns whether the function was successful or not. -bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - vector<UpdateStatus>& matchersToUpdate, - vector<bool>& cycleTracker) { - // Have already examined this matcher. - if (matchersToUpdate[matcherIdx] != UPDATE_UNKNOWN) { - return true; - } - - const AtomMatcher& matcher = config.atom_matcher(matcherIdx); - int64_t id = matcher.id(); - // Check if new matcher. - const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id); - if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) { - matchersToUpdate[matcherIdx] = UPDATE_NEW; - return true; - } - - // This is an existing matcher. Check if it has changed. - string serializedMatcher; - if (!matcher.SerializeToString(&serializedMatcher)) { - ALOGE("Unable to serialize matcher %lld", (long long)id); - return false; - } - uint64_t newProtoHash = Hash64(serializedMatcher); - if (newProtoHash != oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second]->getProtoHash()) { - matchersToUpdate[matcherIdx] = UPDATE_REPLACE; - return true; - } - - switch (matcher.contents_case()) { - case AtomMatcher::ContentsCase::kSimpleAtomMatcher: { - matchersToUpdate[matcherIdx] = UPDATE_PRESERVE; - return true; - } - case AtomMatcher::ContentsCase::kCombination: { - // Recurse to check if children have changed. - cycleTracker[matcherIdx] = true; - UpdateStatus status = UPDATE_PRESERVE; - for (const int64_t childMatcherId : matcher.combination().matcher()) { - const auto& childIt = newAtomMatchingTrackerMap.find(childMatcherId); - if (childIt == newAtomMatchingTrackerMap.end()) { - ALOGW("Matcher %lld not found in the config", (long long)childMatcherId); - return false; - } - const int childIdx = childIt->second; - if (cycleTracker[childIdx]) { - ALOGE("Cycle detected in matcher config"); - return false; - } - if (!determineMatcherUpdateStatus( - config, childIdx, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, - newAtomMatchingTrackerMap, matchersToUpdate, cycleTracker)) { - return false; - } - - if (matchersToUpdate[childIdx] == UPDATE_REPLACE) { - status = UPDATE_REPLACE; - break; - } - } - matchersToUpdate[matcherIdx] = status; - cycleTracker[matcherIdx] = false; - return true; - } - default: { - ALOGE("Matcher \"%lld\" malformed", (long long)id); - return false; - } - } - return true; -} - -bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - set<int>& allTagIds, - unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, - set<int64_t>& replacedMatchers) { - const int atomMatcherCount = config.atom_matcher_size(); - vector<AtomMatcher> matcherProtos; - matcherProtos.reserve(atomMatcherCount); - newAtomMatchingTrackers.reserve(atomMatcherCount); - - // Maps matcher id to their position in the config. For fast lookup of dependencies. - for (int i = 0; i < atomMatcherCount; i++) { - const AtomMatcher& matcher = config.atom_matcher(i); - if (newAtomMatchingTrackerMap.find(matcher.id()) != newAtomMatchingTrackerMap.end()) { - ALOGE("Duplicate atom matcher found for id %lld", (long long)matcher.id()); - return false; - } - newAtomMatchingTrackerMap[matcher.id()] = i; - matcherProtos.push_back(matcher); - } - - // For combination matchers, we need to determine if any children need to be updated. - vector<UpdateStatus> matchersToUpdate(atomMatcherCount, UPDATE_UNKNOWN); - vector<bool> cycleTracker(atomMatcherCount, false); - for (int i = 0; i < atomMatcherCount; i++) { - if (!determineMatcherUpdateStatus(config, i, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)) { - return false; - } - } - - for (int i = 0; i < atomMatcherCount; i++) { - const AtomMatcher& matcher = config.atom_matcher(i); - const int64_t id = matcher.id(); - switch (matchersToUpdate[i]) { - case UPDATE_PRESERVE: { - const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id); - if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) { - ALOGE("Could not find AtomMatcher %lld in the previous config, but expected it " - "to be there", - (long long)id); - return false; - } - const sp<AtomMatchingTracker>& tracker = - oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second]; - if (!tracker->onConfigUpdated(matcherProtos[i], i, newAtomMatchingTrackerMap)) { - ALOGW("Config update failed for matcher %lld", (long long)id); - return false; - } - newAtomMatchingTrackers.push_back(tracker); - break; - } - case UPDATE_REPLACE: - replacedMatchers.insert(id); - [[fallthrough]]; // Intentionally fallthrough to create the new matcher. - case UPDATE_NEW: { - sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, i, uidMap); - if (tracker == nullptr) { - return false; - } - newAtomMatchingTrackers.push_back(tracker); - break; - } - default: { - ALOGE("Matcher \"%lld\" update state is unknown. This should never happen", - (long long)id); - return false; - } - } - } - - std::fill(cycleTracker.begin(), cycleTracker.end(), false); - for (auto& matcher : newAtomMatchingTrackers) { - if (!matcher->init(matcherProtos, newAtomMatchingTrackers, newAtomMatchingTrackerMap, - cycleTracker)) { - return false; - } - // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only. - const set<int>& tagIds = matcher->getAtomIds(); - allTagIds.insert(tagIds.begin(), tagIds.end()); - } - - return true; -} - -// Recursive function to determine if a condition needs to be updated. Populates conditionsToUpdate. -// Returns whether the function was successful or not. -bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx, - const unordered_map<int64_t, int>& oldConditionTrackerMap, - const vector<sp<ConditionTracker>>& oldConditionTrackers, - const unordered_map<int64_t, int>& newConditionTrackerMap, - const set<int64_t>& replacedMatchers, - vector<UpdateStatus>& conditionsToUpdate, - vector<bool>& cycleTracker) { - // Have already examined this condition. - if (conditionsToUpdate[conditionIdx] != UPDATE_UNKNOWN) { - return true; - } - - const Predicate& predicate = config.predicate(conditionIdx); - int64_t id = predicate.id(); - // Check if new condition. - const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id); - if (oldConditionTrackerIt == oldConditionTrackerMap.end()) { - conditionsToUpdate[conditionIdx] = UPDATE_NEW; - return true; - } - - // This is an existing condition. Check if it has changed. - string serializedCondition; - if (!predicate.SerializeToString(&serializedCondition)) { - ALOGE("Unable to serialize matcher %lld", (long long)id); - return false; - } - uint64_t newProtoHash = Hash64(serializedCondition); - if (newProtoHash != oldConditionTrackers[oldConditionTrackerIt->second]->getProtoHash()) { - conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; - return true; - } - - switch (predicate.contents_case()) { - case Predicate::ContentsCase::kSimplePredicate: { - // Need to check if any of the underlying matchers changed. - const SimplePredicate& simplePredicate = predicate.simple_predicate(); - if (simplePredicate.has_start()) { - if (replacedMatchers.find(simplePredicate.start()) != replacedMatchers.end()) { - conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; - return true; - } - } - if (simplePredicate.has_stop()) { - if (replacedMatchers.find(simplePredicate.stop()) != replacedMatchers.end()) { - conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; - return true; - } - } - if (simplePredicate.has_stop_all()) { - if (replacedMatchers.find(simplePredicate.stop_all()) != replacedMatchers.end()) { - conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; - return true; - } - } - conditionsToUpdate[conditionIdx] = UPDATE_PRESERVE; - return true; - } - case Predicate::ContentsCase::kCombination: { - // Need to recurse on the children to see if any of the child predicates changed. - cycleTracker[conditionIdx] = true; - UpdateStatus status = UPDATE_PRESERVE; - for (const int64_t childPredicateId : predicate.combination().predicate()) { - const auto& childIt = newConditionTrackerMap.find(childPredicateId); - if (childIt == newConditionTrackerMap.end()) { - ALOGW("Predicate %lld not found in the config", (long long)childPredicateId); - return false; - } - const int childIdx = childIt->second; - if (cycleTracker[childIdx]) { - ALOGE("Cycle detected in predicate config"); - return false; - } - if (!determineConditionUpdateStatus(config, childIdx, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, - cycleTracker)) { - return false; - } - - if (conditionsToUpdate[childIdx] == UPDATE_REPLACE) { - status = UPDATE_REPLACE; - break; - } - } - conditionsToUpdate[conditionIdx] = status; - cycleTracker[conditionIdx] = false; - return true; - } - default: { - ALOGE("Predicate \"%lld\" malformed", (long long)id); - return false; - } - } - - return true; -} - -bool updateConditions(const ConfigKey& key, const StatsdConfig& config, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - const set<int64_t>& replacedMatchers, - const unordered_map<int64_t, int>& oldConditionTrackerMap, - const vector<sp<ConditionTracker>>& oldConditionTrackers, - unordered_map<int64_t, int>& newConditionTrackerMap, - vector<sp<ConditionTracker>>& newConditionTrackers, - unordered_map<int, vector<int>>& trackerToConditionMap, - vector<ConditionState>& conditionCache, set<int64_t>& replacedConditions) { - vector<Predicate> conditionProtos; - const int conditionTrackerCount = config.predicate_size(); - conditionProtos.reserve(conditionTrackerCount); - newConditionTrackers.reserve(conditionTrackerCount); - conditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated); - - for (int i = 0; i < conditionTrackerCount; i++) { - const Predicate& condition = config.predicate(i); - if (newConditionTrackerMap.find(condition.id()) != newConditionTrackerMap.end()) { - ALOGE("Duplicate Predicate found!"); - return false; - } - newConditionTrackerMap[condition.id()] = i; - conditionProtos.push_back(condition); - } - - vector<UpdateStatus> conditionsToUpdate(conditionTrackerCount, UPDATE_UNKNOWN); - vector<bool> cycleTracker(conditionTrackerCount, false); - for (int i = 0; i < conditionTrackerCount; i++) { - if (!determineConditionUpdateStatus(config, i, oldConditionTrackerMap, oldConditionTrackers, - newConditionTrackerMap, replacedMatchers, - conditionsToUpdate, cycleTracker)) { - return false; - } - } - - // Update status has been determined for all conditions. Now perform the update. - set<int> preservedConditions; - for (int i = 0; i < conditionTrackerCount; i++) { - const Predicate& predicate = config.predicate(i); - const int64_t id = predicate.id(); - switch (conditionsToUpdate[i]) { - case UPDATE_PRESERVE: { - preservedConditions.insert(i); - const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id); - if (oldConditionTrackerIt == oldConditionTrackerMap.end()) { - ALOGE("Could not find Predicate %lld in the previous config, but expected it " - "to be there", - (long long)id); - return false; - } - const int oldIndex = oldConditionTrackerIt->second; - newConditionTrackers.push_back(oldConditionTrackers[oldIndex]); - break; - } - case UPDATE_REPLACE: - replacedConditions.insert(id); - [[fallthrough]]; // Intentionally fallthrough to create the new condition tracker. - case UPDATE_NEW: { - sp<ConditionTracker> tracker = - createConditionTracker(key, predicate, i, atomMatchingTrackerMap); - if (tracker == nullptr) { - return false; - } - newConditionTrackers.push_back(tracker); - break; - } - default: { - ALOGE("Condition \"%lld\" update state is unknown. This should never happen", - (long long)id); - return false; - } - } - } - - // Update indices of preserved predicates. - for (const int conditionIndex : preservedConditions) { - if (!newConditionTrackers[conditionIndex]->onConfigUpdated( - conditionProtos, conditionIndex, newConditionTrackers, atomMatchingTrackerMap, - newConditionTrackerMap)) { - ALOGE("Failed to update condition %lld", - (long long)newConditionTrackers[conditionIndex]->getConditionId()); - return false; - } - } - - std::fill(cycleTracker.begin(), cycleTracker.end(), false); - for (int conditionIndex = 0; conditionIndex < conditionTrackerCount; conditionIndex++) { - const sp<ConditionTracker>& conditionTracker = newConditionTrackers[conditionIndex]; - // Calling init on preserved conditions is OK. It is needed to fill the condition cache. - if (!conditionTracker->init(conditionProtos, newConditionTrackers, newConditionTrackerMap, - cycleTracker, conditionCache)) { - return false; - } - for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) { - vector<int>& conditionList = trackerToConditionMap[trackerIndex]; - conditionList.push_back(conditionIndex); - } - } - return true; -} - -bool updateStates(const StatsdConfig& config, const map<int64_t, uint64_t>& oldStateProtoHashes, - unordered_map<int64_t, int>& stateAtomIdMap, - unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - map<int64_t, uint64_t>& newStateProtoHashes, set<int64_t>& replacedStates) { - // Share with metrics_manager_util. - if (!initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes)) { - return false; - } - - for (const auto& [stateId, stateHash] : oldStateProtoHashes) { - const auto& it = newStateProtoHashes.find(stateId); - if (it != newStateProtoHashes.end() && it->second != stateHash) { - replacedStates.insert(stateId); - } - } - return true; -} -// Returns true if any matchers in the metric activation were replaced. -bool metricActivationDepsChange(const StatsdConfig& config, - const unordered_map<int64_t, int>& metricToActivationMap, - const int64_t metricId, const set<int64_t>& replacedMatchers) { - const auto& metricActivationIt = metricToActivationMap.find(metricId); - if (metricActivationIt == metricToActivationMap.end()) { - return false; - } - const MetricActivation& metricActivation = config.metric_activation(metricActivationIt->second); - for (int i = 0; i < metricActivation.event_activation_size(); i++) { - const EventActivation& activation = metricActivation.event_activation(i); - if (replacedMatchers.find(activation.atom_matcher_id()) != replacedMatchers.end()) { - return true; - } - if (activation.has_deactivation_atom_matcher_id()) { - if (replacedMatchers.find(activation.deactivation_atom_matcher_id()) != - replacedMatchers.end()) { - return true; - } - } - } - return false; -} - -bool determineMetricUpdateStatus( - const StatsdConfig& config, const MessageLite& metric, const int64_t metricId, - const MetricType metricType, const set<int64_t>& matcherDependencies, - const set<int64_t>& conditionDependencies, - const ::google::protobuf::RepeatedField<int64_t>& stateDependencies, - const ::google::protobuf::RepeatedPtrField<MetricConditionLink>& conditionLinks, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - const unordered_map<int64_t, int>& metricToActivationMap, - const set<int64_t>& replacedMatchers, const set<int64_t>& replacedConditions, - const set<int64_t>& replacedStates, UpdateStatus& updateStatus) { - // Check if new metric - const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId); - if (oldMetricProducerIt == oldMetricProducerMap.end()) { - updateStatus = UPDATE_NEW; - return true; - } - - // This is an existing metric, check if it has changed. - uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metricId, metricToActivationMap, metricHash)) { - return false; - } - const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second]; - if (oldMetricProducer->getMetricType() != metricType || - oldMetricProducer->getProtoHash() != metricHash) { - updateStatus = UPDATE_REPLACE; - return true; - } - - // Take intersections of the matchers/predicates/states that the metric - // depends on with those that have been replaced. If a metric depends on any - // replaced component, it too must be replaced. - set<int64_t> intersection; - set_intersection(matcherDependencies.begin(), matcherDependencies.end(), - replacedMatchers.begin(), replacedMatchers.end(), - inserter(intersection, intersection.begin())); - if (intersection.size() > 0) { - updateStatus = UPDATE_REPLACE; - return true; - } - set_intersection(conditionDependencies.begin(), conditionDependencies.end(), - replacedConditions.begin(), replacedConditions.end(), - inserter(intersection, intersection.begin())); - if (intersection.size() > 0) { - updateStatus = UPDATE_REPLACE; - return true; - } - set_intersection(stateDependencies.begin(), stateDependencies.end(), replacedStates.begin(), - replacedStates.end(), inserter(intersection, intersection.begin())); - if (intersection.size() > 0) { - updateStatus = UPDATE_REPLACE; - return true; - } - - for (const auto& metricConditionLink : conditionLinks) { - if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) { - updateStatus = UPDATE_REPLACE; - return true; - } - } - - if (metricActivationDepsChange(config, metricToActivationMap, metricId, replacedMatchers)) { - updateStatus = UPDATE_REPLACE; - return true; - } - - updateStatus = UPDATE_PRESERVE; - return true; -} - -bool determineAllMetricUpdateStatuses(const StatsdConfig& config, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - const unordered_map<int64_t, int>& metricToActivationMap, - const set<int64_t>& replacedMatchers, - const set<int64_t>& replacedConditions, - const set<int64_t>& replacedStates, - vector<UpdateStatus>& metricsToUpdate) { - int metricIndex = 0; - for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) { - const CountMetric& metric = config.count_metric(i); - set<int64_t> conditionDependencies; - if (metric.has_condition()) { - conditionDependencies.insert(metric.condition()); - } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_COUNT, {metric.what()}, - conditionDependencies, metric.slice_by_state(), metric.links(), - oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; - } - } - for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) { - const DurationMetric& metric = config.duration_metric(i); - set<int64_t> conditionDependencies({metric.what()}); - if (metric.has_condition()) { - conditionDependencies.insert(metric.condition()); - } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_DURATION, /*matcherDependencies=*/{}, - conditionDependencies, metric.slice_by_state(), metric.links(), - oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; - } - } - for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { - const EventMetric& metric = config.event_metric(i); - set<int64_t> conditionDependencies; - if (metric.has_condition()) { - conditionDependencies.insert(metric.condition()); - } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_EVENT, {metric.what()}, - conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(), - metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; - } - } - for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) { - const ValueMetric& metric = config.value_metric(i); - set<int64_t> conditionDependencies; - if (metric.has_condition()) { - conditionDependencies.insert(metric.condition()); - } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_VALUE, {metric.what()}, - conditionDependencies, metric.slice_by_state(), metric.links(), - oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; - } - } - for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) { - const GaugeMetric& metric = config.gauge_metric(i); - set<int64_t> conditionDependencies; - if (metric.has_condition()) { - conditionDependencies.insert(metric.condition()); - } - set<int64_t> matcherDependencies({metric.what()}); - if (metric.has_trigger_event()) { - matcherDependencies.insert(metric.trigger_event()); - } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_GAUGE, matcherDependencies, - conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(), - metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; - } - } - return true; -} - -// Called when a metric is preserved during a config update. Finds the metric in oldMetricProducers -// and calls onConfigUpdated to update all indices. -optional<sp<MetricProducer>> updateMetric( - const StatsdConfig& config, const int configIndex, const int metricIndex, - const int64_t metricId, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const sp<EventMatcherWizard>& matcherWizard, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId); - if (oldMetricProducerIt == oldMetricProducerMap.end()) { - ALOGE("Could not find Metric %lld in the previous config, but expected it " - "to be there", - (long long)metricId); - return nullopt; - } - const int oldIndex = oldMetricProducerIt->second; - sp<MetricProducer> producer = oldMetricProducers[oldIndex]; - if (!producer->onConfigUpdated(config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, - matcherWizard, allConditionTrackers, conditionTrackerMap, wizard, - metricToActivationMap, trackerToMetricMap, conditionToMetricMap, - activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return nullopt; - } - return {producer}; -} - -bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const set<int64_t>& replacedMatchers, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, - const set<int64_t>& replacedConditions, - vector<sp<ConditionTracker>>& allConditionTrackers, - const vector<ConditionState>& initialConditionCache, - const unordered_map<int64_t, int>& stateAtomIdMap, - const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - const set<int64_t>& replacedStates, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - unordered_map<int64_t, int>& newMetricProducerMap, - vector<sp<MetricProducer>>& newMetricProducers, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - set<int64_t>& noReportMetricIds, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation, set<int64_t>& replacedMetrics) { - sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers); - sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers); - const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + - config.event_metric_size() + config.gauge_metric_size() + - config.value_metric_size(); - newMetricProducers.reserve(allMetricsCount); - - // Construct map from metric id to metric activation index. The map will be used to determine - // the metric activation corresponding to a metric. - unordered_map<int64_t, int> metricToActivationMap; - for (int i = 0; i < config.metric_activation_size(); i++) { - const MetricActivation& metricActivation = config.metric_activation(i); - int64_t metricId = metricActivation.metric_id(); - if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) { - ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId); - return false; - } - metricToActivationMap.insert({metricId, i}); - } - - vector<UpdateStatus> metricsToUpdate(allMetricsCount, UPDATE_UNKNOWN); - if (!determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, replacedMatchers, - replacedConditions, replacedStates, metricsToUpdate)) { - return false; - } - - // Now, perform the update. Must iterate the metric types in the same order - int metricIndex = 0; - for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) { - const CountMetric& metric = config.count_metric(i); - newMetricProducerMap[metric.id()] = metricIndex; - optional<sp<MetricProducer>> producer; - switch (metricsToUpdate[metricIndex]) { - case UPDATE_PRESERVE: { - producer = updateMetric( - config, i, metricIndex, metric.id(), allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, trackerToMetricMap, - conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - break; - } - case UPDATE_REPLACE: - replacedMetrics.insert(metric.id()); - [[fallthrough]]; // Intentionally fallthrough to create the new metric producer. - case UPDATE_NEW: { - producer = createCountMetricProducerAndUpdateMetadata( - key, config, timeBaseNs, currentTimeNs, metric, metricIndex, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, - conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, - allStateGroupMaps, metricToActivationMap, trackerToMetricMap, - conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - break; - } - default: { - ALOGE("Metric \"%lld\" update state is unknown. This should never happen", - (long long)metric.id()); - return false; - } - } - if (!producer) { - return false; - } - newMetricProducers.push_back(producer.value()); - } - for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) { - const DurationMetric& metric = config.duration_metric(i); - newMetricProducerMap[metric.id()] = metricIndex; - optional<sp<MetricProducer>> producer; - switch (metricsToUpdate[metricIndex]) { - case UPDATE_PRESERVE: { - producer = updateMetric( - config, i, metricIndex, metric.id(), allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, trackerToMetricMap, - conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - break; - } - case UPDATE_REPLACE: - replacedMetrics.insert(metric.id()); - [[fallthrough]]; // Intentionally fallthrough to create the new metric producer. - case UPDATE_NEW: { - producer = createDurationMetricProducerAndUpdateMetadata( - key, config, timeBaseNs, currentTimeNs, metric, metricIndex, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, - conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, - allStateGroupMaps, metricToActivationMap, trackerToMetricMap, - conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - break; - } - default: { - ALOGE("Metric \"%lld\" update state is unknown. This should never happen", - (long long)metric.id()); - return false; - } - } - if (!producer) { - return false; - } - newMetricProducers.push_back(producer.value()); - } - for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { - const EventMetric& metric = config.event_metric(i); - newMetricProducerMap[metric.id()] = metricIndex; - optional<sp<MetricProducer>> producer; - switch (metricsToUpdate[metricIndex]) { - case UPDATE_PRESERVE: { - producer = updateMetric( - config, i, metricIndex, metric.id(), allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, trackerToMetricMap, - conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - break; - } - case UPDATE_REPLACE: - replacedMetrics.insert(metric.id()); - [[fallthrough]]; // Intentionally fallthrough to create the new metric producer. - case UPDATE_NEW: { - producer = createEventMetricProducerAndUpdateMetadata( - key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers, - newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, - initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap, - conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - break; - } - default: { - ALOGE("Metric \"%lld\" update state is unknown. This should never happen", - (long long)metric.id()); - return false; - } - } - if (!producer) { - return false; - } - newMetricProducers.push_back(producer.value()); - } - - for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) { - const ValueMetric& metric = config.value_metric(i); - newMetricProducerMap[metric.id()] = metricIndex; - optional<sp<MetricProducer>> producer; - switch (metricsToUpdate[metricIndex]) { - case UPDATE_PRESERVE: { - producer = updateMetric( - config, i, metricIndex, metric.id(), allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, trackerToMetricMap, - conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - break; - } - case UPDATE_REPLACE: - replacedMetrics.insert(metric.id()); - [[fallthrough]]; // Intentionally fallthrough to create the new metric producer. - case UPDATE_NEW: { - producer = createValueMetricProducerAndUpdateMetadata( - key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, - conditionTrackerMap, initialConditionCache, wizard, matcherWizard, - stateAtomIdMap, allStateGroupMaps, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - break; - } - default: { - ALOGE("Metric \"%lld\" update state is unknown. This should never happen", - (long long)metric.id()); - return false; - } - } - if (!producer) { - return false; - } - newMetricProducers.push_back(producer.value()); - } - - for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) { - const GaugeMetric& metric = config.gauge_metric(i); - newMetricProducerMap[metric.id()] = metricIndex; - optional<sp<MetricProducer>> producer; - switch (metricsToUpdate[metricIndex]) { - case UPDATE_PRESERVE: { - producer = updateMetric( - config, i, metricIndex, metric.id(), allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, trackerToMetricMap, - conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - break; - } - case UPDATE_REPLACE: - replacedMetrics.insert(metric.id()); - [[fallthrough]]; // Intentionally fallthrough to create the new metric producer. - case UPDATE_NEW: { - producer = createGaugeMetricProducerAndUpdateMetadata( - key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, - conditionTrackerMap, initialConditionCache, wizard, matcherWizard, - metricToActivationMap, trackerToMetricMap, conditionToMetricMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); - break; - } - default: { - ALOGE("Metric \"%lld\" update state is unknown. This should never happen", - (long long)metric.id()); - return false; - } - } - if (!producer) { - return false; - } - newMetricProducers.push_back(producer.value()); - } - - for (int i = 0; i < config.no_report_metric_size(); ++i) { - const int64_t noReportMetric = config.no_report_metric(i); - if (newMetricProducerMap.find(noReportMetric) == newMetricProducerMap.end()) { - ALOGW("no_report_metric %" PRId64 " not exist", noReportMetric); - return false; - } - noReportMetricIds.insert(noReportMetric); - } - const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(), - config.whitelisted_atom_ids().end()); - for (int i = 0; i < allMetricsCount; i++) { - sp<MetricProducer> producer = newMetricProducers[i]; - // Register metrics to StateTrackers - for (int atomId : producer->getSlicedStateAtoms()) { - // Register listener for atoms that use allowed_log_sources. - // Using atoms allowed from any uid as a sliced state atom is not allowed. - // Redo this check for all metrics in case the atoms allowed from any uid changed. - if (atomsAllowedFromAnyUid.find(atomId) != atomsAllowedFromAnyUid.end()) { - return false; - // Preserved metrics should've already registered.` - } else if (metricsToUpdate[i] != UPDATE_PRESERVE) { - StateManager::getInstance().registerListener(atomId, producer); - } - } - } - - // Init new/replaced metrics. - for (size_t i = 0; i < newMetricProducers.size(); i++) { - if (metricsToUpdate[i] == UPDATE_REPLACE || metricsToUpdate[i] == UPDATE_NEW) { - newMetricProducers[i]->prepareFirstBucket(); - } - } - return true; -} - -bool determineAlertUpdateStatus(const Alert& alert, - const unordered_map<int64_t, int>& oldAlertTrackerMap, - const vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const set<int64_t>& replacedMetrics, UpdateStatus& updateStatus) { - // Check if new alert. - const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id()); - if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) { - updateStatus = UPDATE_NEW; - return true; - } - - // This is an existing alert, check if it has changed. - string serializedAlert; - if (!alert.SerializeToString(&serializedAlert)) { - ALOGW("Unable to serialize alert %lld", (long long)alert.id()); - return false; - } - uint64_t newProtoHash = Hash64(serializedAlert); - const auto [success, oldProtoHash] = - oldAnomalyTrackers[oldAnomalyTrackerIt->second]->getProtoHash(); - if (!success) { - return false; - } - if (newProtoHash != oldProtoHash) { - updateStatus = UPDATE_REPLACE; - return true; - } - - // Check if the metric this alert relies on has changed. - if (replacedMetrics.find(alert.metric_id()) != replacedMetrics.end()) { - updateStatus = UPDATE_REPLACE; - return true; - } - - updateStatus = UPDATE_PRESERVE; - return true; -} - -bool updateAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap, - const set<int64_t>& replacedMetrics, - const unordered_map<int64_t, int>& oldAlertTrackerMap, - const vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - vector<sp<MetricProducer>>& allMetricProducers, - unordered_map<int64_t, int>& newAlertTrackerMap, - vector<sp<AnomalyTracker>>& newAnomalyTrackers) { - int alertCount = config.alert_size(); - vector<UpdateStatus> alertUpdateStatuses(alertCount); - for (int i = 0; i < alertCount; i++) { - if (!determineAlertUpdateStatus(config.alert(i), oldAlertTrackerMap, oldAnomalyTrackers, - replacedMetrics, alertUpdateStatuses[i])) { - return false; - } - } - - for (int i = 0; i < alertCount; i++) { - const Alert& alert = config.alert(i); - newAlertTrackerMap[alert.id()] = newAnomalyTrackers.size(); - switch (alertUpdateStatuses[i]) { - case UPDATE_PRESERVE: { - // Find the alert and update it. - const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id()); - if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) { - ALOGW("Could not find AnomalyTracker %lld in the previous config, but " - "expected it to be there", - (long long)alert.id()); - return false; - } - sp<AnomalyTracker> anomalyTracker = oldAnomalyTrackers[oldAnomalyTrackerIt->second]; - anomalyTracker->onConfigUpdated(); - // Add the alert to the relevant metric. - const auto& metricProducerIt = metricProducerMap.find(alert.metric_id()); - if (metricProducerIt == metricProducerMap.end()) { - ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(), - (long long)alert.metric_id()); - return false; - } - allMetricProducers[metricProducerIt->second]->addAnomalyTracker(anomalyTracker); - newAnomalyTrackers.push_back(anomalyTracker); - break; - } - case UPDATE_REPLACE: - case UPDATE_NEW: { - optional<sp<AnomalyTracker>> anomalyTracker = createAnomalyTracker( - alert, anomalyAlarmMonitor, metricProducerMap, allMetricProducers); - if (!anomalyTracker) { - return false; - } - newAnomalyTrackers.push_back(anomalyTracker.value()); - break; - } - default: { - ALOGE("Alert \"%lld\" update state is unknown. This should never happen", - (long long)alert.id()); - return false; - } - } - } - if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, newAlertTrackerMap, - newAnomalyTrackers)) { - return false; - } - return true; -} - -bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, - const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const vector<sp<ConditionTracker>>& oldConditionTrackers, - const unordered_map<int64_t, int>& oldConditionTrackerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const unordered_map<int64_t, int>& oldAlertTrackerMap, - const map<int64_t, uint64_t>& oldStateProtoHashes, set<int>& allTagIds, - vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, - unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - vector<sp<ConditionTracker>>& newConditionTrackers, - unordered_map<int64_t, int>& newConditionTrackerMap, - vector<sp<MetricProducer>>& newMetricProducers, - unordered_map<int64_t, int>& newMetricProducerMap, - vector<sp<AnomalyTracker>>& newAnomalyTrackers, - unordered_map<int64_t, int>& newAlertTrackerMap, - vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& trackerToConditionMap, - unordered_map<int, vector<int>>& activationTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationTrackerToMetricMap, - vector<int>& metricsWithActivation, - map<int64_t, uint64_t>& newStateProtoHashes, - set<int64_t>& noReportMetricIds) { - set<int64_t> replacedMatchers; - set<int64_t> replacedConditions; - set<int64_t> replacedStates; - set<int64_t> replacedMetrics; - vector<ConditionState> conditionCache; - unordered_map<int64_t, int> stateAtomIdMap; - unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; - - if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, allTagIds, newAtomMatchingTrackerMap, - newAtomMatchingTrackers, replacedMatchers)) { - ALOGE("updateAtomMatchingTrackers failed"); - return false; - } - - if (!updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers, - oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap, - newConditionTrackers, trackerToConditionMap, conditionCache, - replacedConditions)) { - ALOGE("updateConditions failed"); - return false; - } - - if (!updateStates(config, oldStateProtoHashes, stateAtomIdMap, allStateGroupMaps, - newStateProtoHashes, replacedStates)) { - ALOGE("updateStates failed"); - return false; - } - if (!updateMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, - replacedStates, oldMetricProducerMap, oldMetricProducers, - newMetricProducerMap, newMetricProducers, conditionToMetricMap, - trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap, - deactivationTrackerToMetricMap, metricsWithActivation, replacedMetrics)) { - ALOGE("updateMetrics failed"); - return false; - } - - if (!updateAlerts(config, newMetricProducerMap, replacedMetrics, oldAlertTrackerMap, - oldAnomalyTrackers, anomalyAlarmMonitor, newMetricProducers, - newAlertTrackerMap, newAnomalyTrackers)) { - ALOGE("updateAlerts failed"); - return false; - } - - // Alarms do not have any state, so we can reuse the initialization logic. - if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, - newPeriodicAlarmTrackers)) { - ALOGE("initAlarms failed"); - return false; - } - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h deleted file mode 100644 index 8e2be6899699..000000000000 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h +++ /dev/null @@ -1,276 +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. - */ - -#pragma once - -#include <vector> - -#include "anomaly/AlarmMonitor.h" -#include "anomaly/AlarmTracker.h" -#include "condition/ConditionTracker.h" -#include "external/StatsPullerManager.h" -#include "matchers/AtomMatchingTracker.h" -#include "metrics/MetricProducer.h" - -namespace android { -namespace os { -namespace statsd { - -// Helper functions for MetricsManager to update itself from a new StatsdConfig. -// *Note*: only updateStatsdConfig() should be called from outside this file. -// All other functions are intermediate steps, created to make unit testing easier. - -// Possible update states for a component. PRESERVE means we should keep the existing one. -// REPLACE means we should create a new one because the existing one changed -// NEW means we should create a new one because one does not currently exist. -enum UpdateStatus { - UPDATE_UNKNOWN = 0, - UPDATE_PRESERVE = 1, - UPDATE_REPLACE = 2, - UPDATE_NEW = 3, -}; - -// Recursive function to determine if a matcher needs to be updated. -// input: -// [config]: the input StatsdConfig -// [matcherIdx]: the index of the current matcher to be updated -// [oldAtomMatchingTrackerMap]: matcher id to index mapping in the existing MetricsManager -// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers -// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig -// output: -// [matchersToUpdate]: vector of the update status of each matcher. The matcherIdx index will -// be updated from UPDATE_UNKNOWN after this call. -// [cycleTracker]: intermediate param used during recursion. -// Returns whether the function was successful or not. -bool determineMatcherUpdateStatus( - const StatsdConfig& config, const int matcherIdx, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - std::vector<UpdateStatus>& matchersToUpdate, std::vector<bool>& cycleTracker); - -// Updates the AtomMatchingTrackers. -// input: -// [config]: the input StatsdConfig -// [oldAtomMatchingTrackerMap]: existing matcher id to index mapping -// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers -// output: -// [allTagIds]: contains the set of all interesting tag ids to this config. -// [newAtomMatchingTrackerMap]: new matcher id to index mapping -// [newAtomMatchingTrackers]: stores the new AtomMatchingTrackers -// [replacedMatchers]: set of matcher ids that changed and have been replaced -bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - std::set<int>& allTagIds, - std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, - std::set<int64_t>& replacedMatchers); - -// Recursive function to determine if a condition needs to be updated. -// input: -// [config]: the input StatsdConfig -// [conditionIdx]: the index of the current condition to be updated -// [oldConditionTrackerMap]: condition id to index mapping in the existing MetricsManager -// [oldConditionTrackers]: stores the existing ConditionTrackers -// [newConditionTrackerMap]: condition id to index mapping in the input StatsdConfig -// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced -// output: -// [conditionsToUpdate]: vector of the update status of each condition. The conditionIdx index will -// be updated from UPDATE_UNKNOWN after this call. -// [cycleTracker]: intermediate param used during recursion. -// Returns whether the function was successful or not. -bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx, - const std::unordered_map<int64_t, int>& oldConditionTrackerMap, - const std::vector<sp<ConditionTracker>>& oldConditionTrackers, - const std::unordered_map<int64_t, int>& newConditionTrackerMap, - const std::set<int64_t>& replacedMatchers, - std::vector<UpdateStatus>& conditionsToUpdate, - std::vector<bool>& cycleTracker); - -// Updates ConditionTrackers -// input: -// [config]: the input config -// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step. -// [replacedMatchers]: ids of replaced matchers. conditions depending on these must also be replaced -// [oldConditionTrackerMap]: existing matcher id to index mapping -// [oldConditionTrackers]: stores the existing ConditionTrackers -// output: -// [newConditionTrackerMap]: new condition id to index mapping -// [newConditionTrackers]: stores the sp to all the ConditionTrackers -// [trackerToConditionMap]: contains the mapping from the index of an atom matcher -// to indices of condition trackers that use the matcher -// [conditionCache]: stores the current conditions for each ConditionTracker -// [replacedConditions]: set of condition ids that have changed and have been replaced -bool updateConditions(const ConfigKey& key, const StatsdConfig& config, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - const std::set<int64_t>& replacedMatchers, - const std::unordered_map<int64_t, int>& oldConditionTrackerMap, - const std::vector<sp<ConditionTracker>>& oldConditionTrackers, - std::unordered_map<int64_t, int>& newConditionTrackerMap, - std::vector<sp<ConditionTracker>>& newConditionTrackers, - std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - std::vector<ConditionState>& conditionCache, - std::set<int64_t>& replacedConditions); - -bool updateStates(const StatsdConfig& config, - const std::map<int64_t, uint64_t>& oldStateProtoHashes, - std::unordered_map<int64_t, int>& stateAtomIdMap, - std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps, - std::map<int64_t, uint64_t>& newStateProtoHashes, - std::set<int64_t>& replacedStates); - -// Function to determine the update status (preserve/replace/new) of all metrics in the config. -// [config]: the input StatsdConfig -// [oldMetricProducerMap]: metric id to index mapping in the existing MetricsManager -// [oldMetricProducers]: stores the existing MetricProducers -// [metricToActivationMap]: map from metric id to metric activation index -// [replacedMatchers]: set of replaced matcher ids. metrics using these matchers must be replaced -// [replacedConditions]: set of replaced conditions. metrics using these conditions must be replaced -// [replacedStates]: set of replaced state ids. metrics using these states must be replaced -// output: -// [metricsToUpdate]: update status of each metric. Will be changed from UPDATE_UNKNOWN -// Returns whether the function was successful or not. -bool determineAllMetricUpdateStatuses(const StatsdConfig& config, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - const unordered_map<int64_t, int>& metricToActivationMap, - const set<int64_t>& replacedMatchers, - const set<int64_t>& replacedConditions, - const set<int64_t>& replacedStates, - vector<UpdateStatus>& metricsToUpdate); - -// Update MetricProducers. -// input: -// [key]: the config key that this config belongs to -// [config]: the input config -// [timeBaseNs]: start time base for all metrics -// [currentTimeNs]: time of the config update -// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step. -// [replacedMatchers]: ids of replaced matchers. Metrics depending on these must also be replaced -// [allAtomMatchingTrackers]: stores the sp of the atom matchers. -// [conditionTrackerMap]: condition name to index mapping -// [replacedConditions]: set of condition ids that have changed and have been replaced -// [stateAtomIdMap]: contains the mapping from state ids to atom ids -// [allStateGroupMaps]: contains the mapping from atom ids and state values to -// state group ids for all states -// output: -// [allMetricProducers]: contains the list of sp to the MetricProducers created. -// [conditionToMetricMap]: contains the mapping from condition tracker index to -// the list of MetricProducer index -// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index. -bool updateMetrics( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const std::set<int64_t>& replacedMatchers, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const std::set<int64_t>& replacedConditions, - std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::vector<ConditionState>& initialConditionCache, - const std::unordered_map<int64_t, int>& stateAtomIdMap, - const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps, - const std::set<int64_t>& replacedStates, - const std::unordered_map<int64_t, int>& oldMetricProducerMap, - const std::vector<sp<MetricProducer>>& oldMetricProducers, - std::unordered_map<int64_t, int>& newMetricProducerMap, - std::vector<sp<MetricProducer>>& newMetricProducers, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::set<int64_t>& noReportMetricIds, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation, std::set<int64_t>& replacedMetrics); - -// Function to determine the update status (preserve/replace/new) of an alert. -// [alert]: the input Alert -// [oldAlertTrackerMap]: alert id to index mapping in the existing MetricsManager -// [oldAnomalyTrackers]: stores the existing AnomalyTrackers -// [replacedMetrics]: set of replaced metric ids. alerts using these metrics must be replaced -// output: -// [updateStatus]: update status of the alert. Will be changed from UPDATE_UNKNOWN -// Returns whether the function was successful or not. -bool determineAlertUpdateStatus(const Alert& alert, - const std::unordered_map<int64_t, int>& oldAlertTrackerMap, - const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const std::set<int64_t>& replacedMetrics, - UpdateStatus& updateStatus); - -// Update MetricProducers. -// input: -// [config]: the input config -// [metricProducerMap]: metric id to index mapping in the new config -// [replacedMetrics]: set of metric ids that have changed and were replaced -// [oldAlertTrackerMap]: alert id to index mapping in the existing MetricsManager. -// [oldAnomalyTrackers]: stores the existing AnomalyTrackers -// [anomalyAlarmMonitor]: AlarmMonitor used for duration metric anomaly detection -// [allMetricProducers]: stores the sp of the metric producers, AnomalyTrackers need to be added. -// [stateAtomIdMap]: contains the mapping from state ids to atom ids -// [allStateGroupMaps]: contains the mapping from atom ids and state values to -// state group ids for all states -// output: -// [newAlertTrackerMap]: mapping of alert id to index in the new config -// [newAnomalyTrackers]: contains the list of sp to the AnomalyTrackers created. -bool updateAlerts(const StatsdConfig& config, - const std::unordered_map<int64_t, int>& metricProducerMap, - const std::set<int64_t>& replacedMetrics, - const std::unordered_map<int64_t, int>& oldAlertTrackerMap, - const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - std::vector<sp<MetricProducer>>& allMetricProducers, - std::unordered_map<int64_t, int>& newAlertTrackerMap, - std::vector<sp<AnomalyTracker>>& newAnomalyTrackers); - -// Updates the existing MetricsManager from a new StatsdConfig. -// Parameters are the members of MetricsManager. See MetricsManager for declaration. -bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, - const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::vector<sp<ConditionTracker>>& oldConditionTrackers, - const std::unordered_map<int64_t, int>& oldConditionTrackerMap, - const std::vector<sp<MetricProducer>>& oldMetricProducers, - const std::unordered_map<int64_t, int>& oldMetricProducerMap, - const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const std::unordered_map<int64_t, int>& oldAlertTrackerMap, - const std::map<int64_t, uint64_t>& oldStateProtoHashes, - std::set<int>& allTagIds, - std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, - std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - std::vector<sp<ConditionTracker>>& newConditionTrackers, - std::unordered_map<int64_t, int>& newConditionTrackerMap, - std::vector<sp<MetricProducer>>& newMetricProducers, - std::unordered_map<int64_t, int>& newMetricProducerMap, - std::vector<sp<AnomalyTracker>>& newAlertTrackers, - std::unordered_map<int64_t, int>& newAlertTrackerMap, - std::vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - std::unordered_map<int, std::vector<int>>& activationTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationTrackerToMetricMap, - std::vector<int>& metricsWithActivation, - std::map<int64_t, uint64_t>& newStateProtoHashes, - std::set<int64_t>& noReportMetricIds); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp deleted file mode 100644 index 4474df4346cf..000000000000 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ /dev/null @@ -1,1220 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "metrics_manager_util.h" - -#include <inttypes.h> - -#include "FieldValue.h" -#include "condition/CombinationConditionTracker.h" -#include "condition/SimpleConditionTracker.h" -#include "external/StatsPullerManager.h" -#include "hash.h" -#include "matchers/CombinationAtomMatchingTracker.h" -#include "matchers/EventMatcherWizard.h" -#include "matchers/SimpleAtomMatchingTracker.h" -#include "metrics/CountMetricProducer.h" -#include "metrics/DurationMetricProducer.h" -#include "metrics/EventMetricProducer.h" -#include "metrics/GaugeMetricProducer.h" -#include "metrics/MetricProducer.h" -#include "metrics/ValueMetricProducer.h" -#include "state/StateManager.h" -#include "stats_util.h" - -using google::protobuf::MessageLite; -using std::set; -using std::unordered_map; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -namespace { - -bool hasLeafNode(const FieldMatcher& matcher) { - if (!matcher.has_field()) { - return false; - } - for (int i = 0; i < matcher.child_size(); ++i) { - if (hasLeafNode(matcher.child(i))) { - return true; - } - } - return true; -} - -} // namespace - -sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index, - const sp<UidMap>& uidMap) { - string serializedMatcher; - if (!logMatcher.SerializeToString(&serializedMatcher)) { - ALOGE("Unable to serialize matcher %lld", (long long)logMatcher.id()); - return nullptr; - } - uint64_t protoHash = Hash64(serializedMatcher); - switch (logMatcher.contents_case()) { - case AtomMatcher::ContentsCase::kSimpleAtomMatcher: - return new SimpleAtomMatchingTracker(logMatcher.id(), index, protoHash, - logMatcher.simple_atom_matcher(), uidMap); - case AtomMatcher::ContentsCase::kCombination: - return new CombinationAtomMatchingTracker(logMatcher.id(), index, protoHash); - default: - ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id()); - return nullptr; - } -} - -sp<ConditionTracker> createConditionTracker( - const ConfigKey& key, const Predicate& predicate, const int index, - const unordered_map<int64_t, int>& atomMatchingTrackerMap) { - string serializedPredicate; - if (!predicate.SerializeToString(&serializedPredicate)) { - ALOGE("Unable to serialize predicate %lld", (long long)predicate.id()); - return nullptr; - } - uint64_t protoHash = Hash64(serializedPredicate); - switch (predicate.contents_case()) { - case Predicate::ContentsCase::kSimplePredicate: { - return new SimpleConditionTracker(key, predicate.id(), protoHash, index, - predicate.simple_predicate(), atomMatchingTrackerMap); - } - case Predicate::ContentsCase::kCombination: { - return new CombinationConditionTracker(predicate.id(), index, protoHash); - } - default: - ALOGE("Predicate \"%lld\" malformed", (long long)predicate.id()); - return nullptr; - } -} - -bool getMetricProtoHash(const StatsdConfig& config, const MessageLite& metric, const int64_t id, - const unordered_map<int64_t, int>& metricToActivationMap, - uint64_t& metricHash) { - string serializedMetric; - if (!metric.SerializeToString(&serializedMetric)) { - ALOGE("Unable to serialize metric %lld", (long long)id); - return false; - } - metricHash = Hash64(serializedMetric); - - // Combine with activation hash, if applicable - const auto& metricActivationIt = metricToActivationMap.find(id); - if (metricActivationIt != metricToActivationMap.end()) { - string serializedActivation; - const MetricActivation& activation = config.metric_activation(metricActivationIt->second); - if (!activation.SerializeToString(&serializedActivation)) { - ALOGE("Unable to serialize metric activation for metric %lld", (long long)id); - return false; - } - metricHash = Hash64(to_string(metricHash).append(to_string(Hash64(serializedActivation)))); - } - return true; -} - -bool handleMetricWithAtomMatchingTrackers( - const int64_t matcherId, const int metricIndex, const bool enforceOneAtom, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - unordered_map<int, vector<int>>& trackerToMetricMap, int& logTrackerIndex) { - auto logTrackerIt = atomMatchingTrackerMap.find(matcherId); - if (logTrackerIt == atomMatchingTrackerMap.end()) { - ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)matcherId); - return false; - } - if (enforceOneAtom && allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) { - ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, " - "the \"what\" can only be about one atom type. trigger_event matchers can also only " - "be about one atom type.", - (long long)matcherId); - return false; - } - logTrackerIndex = logTrackerIt->second; - auto& metric_list = trackerToMetricMap[logTrackerIndex]; - metric_list.push_back(metricIndex); - return true; -} - -bool handleMetricWithConditions( - const int64_t condition, const int metricIndex, - const unordered_map<int64_t, int>& conditionTrackerMap, - const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>& - links, - const vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex, - unordered_map<int, vector<int>>& conditionToMetricMap) { - auto condition_it = conditionTrackerMap.find(condition); - if (condition_it == conditionTrackerMap.end()) { - ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition); - return false; - } - - for (const auto& link : links) { - auto it = conditionTrackerMap.find(link.condition()); - if (it == conditionTrackerMap.end()) { - ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition()); - return false; - } - } - conditionIndex = condition_it->second; - - // will create new vector if not exist before. - auto& metricList = conditionToMetricMap[condition_it->second]; - metricList.push_back(metricIndex); - return true; -} - -// Initializes state data structures for a metric. -// input: -// [config]: the input config -// [stateIds]: the slice_by_state ids for this metric -// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids -// [allStateGroupMaps]: this map contains the mapping from state ids and state -// values to state group ids for all states -// output: -// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states -// [stateGroupMap]: this map should contain the mapping from states ids and state -// values to state group ids for all states that this metric -// is interested in -bool handleMetricWithStates( - const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds, - const unordered_map<int64_t, int>& stateAtomIdMap, - const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - vector<int>& slicedStateAtoms, - unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) { - for (const auto& stateId : stateIds) { - auto it = stateAtomIdMap.find(stateId); - if (it == stateAtomIdMap.end()) { - ALOGW("cannot find State %" PRId64 " in the config", stateId); - return false; - } - int atomId = it->second; - slicedStateAtoms.push_back(atomId); - - auto stateIt = allStateGroupMaps.find(stateId); - if (stateIt != allStateGroupMaps.end()) { - stateGroupMap[atomId] = stateIt->second; - } - } - return true; -} - -bool handleMetricWithStateLink(const FieldMatcher& stateMatcher, - const vector<Matcher>& dimensionsInWhat) { - vector<Matcher> stateMatchers; - translateFieldMatcher(stateMatcher, &stateMatchers); - - return subsetDimensions(stateMatchers, dimensionsInWhat); -} - -// Validates a metricActivation and populates state. -// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer -// to provide the producer with state about its activators and deactivators. -// Returns false if there are errors. -bool handleMetricActivation( - const StatsdConfig& config, const int64_t metricId, const int metricIndex, - const unordered_map<int64_t, int>& metricToActivationMap, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation, - unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) { - // Check if metric has an associated activation - auto itr = metricToActivationMap.find(metricId); - if (itr == metricToActivationMap.end()) { - return true; - } - - int activationIndex = itr->second; - const MetricActivation& metricActivation = config.metric_activation(activationIndex); - - for (int i = 0; i < metricActivation.event_activation_size(); i++) { - const EventActivation& activation = metricActivation.event_activation(i); - - auto itr = atomMatchingTrackerMap.find(activation.atom_matcher_id()); - if (itr == atomMatchingTrackerMap.end()) { - ALOGE("Atom matcher not found for event activation."); - return false; - } - - ActivationType activationType = (activation.has_activation_type()) - ? activation.activation_type() - : metricActivation.activation_type(); - std::shared_ptr<Activation> activationWrapper = - std::make_shared<Activation>(activationType, activation.ttl_seconds() * NS_PER_SEC); - - int atomMatcherIndex = itr->second; - activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex); - eventActivationMap.emplace(atomMatcherIndex, activationWrapper); - - if (activation.has_deactivation_atom_matcher_id()) { - itr = atomMatchingTrackerMap.find(activation.deactivation_atom_matcher_id()); - if (itr == atomMatchingTrackerMap.end()) { - ALOGE("Atom matcher not found for event deactivation."); - return false; - } - int deactivationAtomMatcherIndex = itr->second; - deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex); - eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper); - } - } - - metricsWithActivation.push_back(metricIndex); - return true; -} - -// Validates a metricActivation and populates state. -// Fills the new event activation/deactivation maps, preserving the existing activations -// Returns false if there are errors. -bool handleMetricActivationOnConfigUpdate( - const StatsdConfig& config, const int64_t metricId, const int metricIndex, - const unordered_map<int64_t, int>& metricToActivationMap, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation, - unordered_map<int, shared_ptr<Activation>>& newEventActivationMap, - unordered_map<int, vector<shared_ptr<Activation>>>& newEventDeactivationMap) { - // Check if metric has an associated activation. - const auto& itr = metricToActivationMap.find(metricId); - if (itr == metricToActivationMap.end()) { - return true; - } - - int activationIndex = itr->second; - const MetricActivation& metricActivation = config.metric_activation(activationIndex); - - for (int i = 0; i < metricActivation.event_activation_size(); i++) { - const int64_t activationMatcherId = metricActivation.event_activation(i).atom_matcher_id(); - - const auto& newActivationIt = newAtomMatchingTrackerMap.find(activationMatcherId); - if (newActivationIt == newAtomMatchingTrackerMap.end()) { - ALOGE("Atom matcher not found in new config for event activation."); - return false; - } - int newActivationMatcherIndex = newActivationIt->second; - - // Find the old activation struct and copy it over. - const auto& oldActivationIt = oldAtomMatchingTrackerMap.find(activationMatcherId); - if (oldActivationIt == oldAtomMatchingTrackerMap.end()) { - ALOGE("Atom matcher not found in existing config for event activation."); - return false; - } - int oldActivationMatcherIndex = oldActivationIt->second; - const auto& oldEventActivationIt = oldEventActivationMap.find(oldActivationMatcherIndex); - if (oldEventActivationIt == oldEventActivationMap.end()) { - ALOGE("Could not find existing event activation to update"); - return false; - } - newEventActivationMap.emplace(newActivationMatcherIndex, oldEventActivationIt->second); - activationAtomTrackerToMetricMap[newActivationMatcherIndex].push_back(metricIndex); - - if (metricActivation.event_activation(i).has_deactivation_atom_matcher_id()) { - const int64_t deactivationMatcherId = - metricActivation.event_activation(i).deactivation_atom_matcher_id(); - const auto& newDeactivationIt = newAtomMatchingTrackerMap.find(deactivationMatcherId); - if (newDeactivationIt == newAtomMatchingTrackerMap.end()) { - ALOGE("Deactivation atom matcher not found in new config for event activation."); - return false; - } - int newDeactivationMatcherIndex = newDeactivationIt->second; - newEventDeactivationMap[newDeactivationMatcherIndex].push_back( - oldEventActivationIt->second); - deactivationAtomTrackerToMetricMap[newDeactivationMatcherIndex].push_back(metricIndex); - } - } - - metricsWithActivation.push_back(metricIndex); - return true; -} - -optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, - const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const unordered_map<int64_t, int>& stateAtomIdMap, - const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - if (!metric.has_id() || !metric.has_what()) { - ALOGE("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id()); - return nullopt; - } - int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { - return nullopt; - } - - int conditionIndex = -1; - if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { - return nullopt; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return nullopt; - } - } - - std::vector<int> slicedStateAtoms; - unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; - if (metric.slice_by_state_size() > 0) { - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { - return nullopt; - } - } else { - if (metric.state_link_size() > 0) { - ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state"); - return nullopt; - } - } - - unordered_map<int, shared_ptr<Activation>> eventActivationMap; - unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap)) { - return nullopt; - } - - uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { - return nullopt; - } - - return {new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, - metricHash, timeBaseNs, currentTimeNs, eventActivationMap, - eventDeactivationMap, slicedStateAtoms, stateGroupMap)}; -} - -optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, - const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const unordered_map<int64_t, int>& stateAtomIdMap, - const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - if (!metric.has_id() || !metric.has_what()) { - ALOGE("cannot find metric id or \"what\" in DurationMetric \"%lld\"", - (long long)metric.id()); - return nullopt; - } - const auto& what_it = conditionTrackerMap.find(metric.what()); - if (what_it == conditionTrackerMap.end()) { - ALOGE("DurationMetric's \"what\" is not present in the condition trackers"); - return nullopt; - } - - const Predicate& durationWhat = config.predicate(what_it->second); - if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { - ALOGE("DurationMetric's \"what\" must be a simple condition"); - return nullopt; - } - - const SimplePredicate& simplePredicate = durationWhat.simple_predicate(); - bool nesting = simplePredicate.count_nesting(); - - int startIndex = -1, stopIndex = -1, stopAllIndex = -1; - if (!simplePredicate.has_start() || - !handleMetricWithAtomMatchingTrackers( - simplePredicate.start(), metricIndex, metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, startIndex)) { - ALOGE("Duration metrics must specify a valid start event matcher"); - return nullopt; - } - - if (simplePredicate.has_stop() && - !handleMetricWithAtomMatchingTrackers( - simplePredicate.stop(), metricIndex, metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, stopIndex)) { - return nullopt; - } - - if (simplePredicate.has_stop_all() && - !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, stopAllIndex)) { - return nullopt; - } - - FieldMatcher internalDimensions = simplePredicate.dimensions(); - - int conditionIndex = -1; - if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { - return nullopt; - } - } else if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return nullopt; - } - - std::vector<int> slicedStateAtoms; - unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; - if (metric.slice_by_state_size() > 0) { - if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) { - ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state"); - return nullopt; - } - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { - return nullopt; - } - } else if (metric.state_link_size() > 0) { - ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state"); - return nullopt; - } - - // Check that all metric state links are a subset of dimensions_in_what fields. - std::vector<Matcher> dimensionsInWhat; - translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); - for (const auto& stateLink : metric.state_link()) { - if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { - return nullopt; - } - } - - unordered_map<int, shared_ptr<Activation>> eventActivationMap; - unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap)) { - return nullopt; - } - - uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { - return nullopt; - } - - return {new DurationMetricProducer( - key, metric, conditionIndex, initialConditionCache, startIndex, stopIndex, stopAllIndex, - nesting, wizard, metricHash, internalDimensions, timeBaseNs, currentTimeNs, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap)}; -} - -optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const EventMetric& metric, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, - const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - if (!metric.has_id() || !metric.has_what()) { - ALOGE("cannot find the metric name or what in config"); - return nullopt; - } - int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { - return nullopt; - } - - int conditionIndex = -1; - if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { - return nullopt; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return nullopt; - } - } - - unordered_map<int, shared_ptr<Activation>> eventActivationMap; - unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - bool success = handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap); - if (!success) return nullptr; - - uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { - return nullopt; - } - - return {new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, - metricHash, timeBaseNs, eventActivationMap, - eventDeactivationMap)}; -} - -optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const ValueMetric& metric, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, - const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const sp<EventMatcherWizard>& matcherWizard, - const unordered_map<int64_t, int>& stateAtomIdMap, - const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - if (!metric.has_id() || !metric.has_what()) { - ALOGE("cannot find metric id or \"what\" in ValueMetric \"%lld\"", (long long)metric.id()); - return nullopt; - } - if (!metric.has_value_field()) { - ALOGE("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id()); - return nullopt; - } - std::vector<Matcher> fieldMatchers; - translateFieldMatcher(metric.value_field(), &fieldMatchers); - if (fieldMatchers.size() < 1) { - ALOGE("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id()); - return nullopt; - } - - int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { - return nullopt; - } - - sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex); - // If it is pulled atom, it should be simple matcher with one tagId. - if (atomMatcher->getAtomIds().size() != 1) { - return nullopt; - } - int atomTagId = *(atomMatcher->getAtomIds().begin()); - int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1; - - int conditionIndex = -1; - if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { - return nullopt; - } - } else if (metric.links_size() > 0) { - ALOGE("metrics has a MetricConditionLink but doesn't have a condition"); - return nullopt; - } - - std::vector<int> slicedStateAtoms; - unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; - if (metric.slice_by_state_size() > 0) { - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { - return nullopt; - } - } else if (metric.state_link_size() > 0) { - ALOGE("ValueMetric has a MetricStateLink but doesn't have a sliced state"); - return nullopt; - } - - // Check that all metric state links are a subset of dimensions_in_what fields. - std::vector<Matcher> dimensionsInWhat; - translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); - for (const auto& stateLink : metric.state_link()) { - if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { - return nullopt; - } - } - - unordered_map<int, shared_ptr<Activation>> eventActivationMap; - unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap)) { - return nullopt; - } - - uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { - return nullopt; - } - - return {new ValueMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, - metricHash, trackerIndex, matcherWizard, pullTagId, timeBaseNs, - currentTimeNs, pullerManager, eventActivationMap, - eventDeactivationMap, slicedStateAtoms, stateGroupMap)}; -} - -optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const GaugeMetric& metric, const int metricIndex, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, - const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const sp<EventMatcherWizard>& matcherWizard, - const unordered_map<int64_t, int>& metricToActivationMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - if (!metric.has_id() || !metric.has_what()) { - ALOGE("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id()); - return nullopt; - } - - if ((!metric.gauge_fields_filter().has_include_all() || - (metric.gauge_fields_filter().include_all() == false)) && - !hasLeafNode(metric.gauge_fields_filter().fields())) { - ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id()); - return nullopt; - } - if ((metric.gauge_fields_filter().has_include_all() && - metric.gauge_fields_filter().include_all() == true) && - hasLeafNode(metric.gauge_fields_filter().fields())) { - ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id()); - return nullopt; - } - - int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { - return nullopt; - } - - sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex); - // For GaugeMetric atom, it should be simple matcher with one tagId. - if (atomMatcher->getAtomIds().size() != 1) { - return nullopt; - } - int atomTagId = *(atomMatcher->getAtomIds().begin()); - int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1; - - int triggerTrackerIndex; - int triggerAtomId = -1; - if (metric.has_trigger_event()) { - if (pullTagId == -1) { - ALOGW("Pull atom not specified for trigger"); - return nullopt; - } - // trigger_event should be used with FIRST_N_SAMPLES - if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) { - ALOGW("Gauge Metric with trigger event must have sampling type FIRST_N_SAMPLES"); - return nullopt; - } - if (!handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex, - /*enforceOneAtom=*/true, allAtomMatchingTrackers, - atomMatchingTrackerMap, trackerToMetricMap, - triggerTrackerIndex)) { - return nullopt; - } - sp<AtomMatchingTracker> triggerAtomMatcher = - allAtomMatchingTrackers.at(triggerTrackerIndex); - triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin()); - } - - if (!metric.has_trigger_event() && pullTagId != -1 && - metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) { - ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger"); - return nullopt; - } - - int conditionIndex = -1; - if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { - return nullopt; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return nullopt; - } - } - - unordered_map<int, shared_ptr<Activation>> eventActivationMap; - unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap)) { - return nullopt; - } - - uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { - return nullopt; - } - - return {new GaugeMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, - metricHash, trackerIndex, matcherWizard, pullTagId, - triggerAtomId, atomTagId, timeBaseNs, currentTimeNs, - pullerManager, eventActivationMap, eventDeactivationMap)}; -} - -optional<sp<AnomalyTracker>> createAnomalyTracker( - const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor, - const unordered_map<int64_t, int>& metricProducerMap, - vector<sp<MetricProducer>>& allMetricProducers) { - const auto& itr = metricProducerMap.find(alert.metric_id()); - if (itr == metricProducerMap.end()) { - ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(), - (long long)alert.metric_id()); - return nullopt; - } - if (!alert.has_trigger_if_sum_gt()) { - ALOGW("invalid alert: missing threshold"); - return nullopt; - } - if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) { - ALOGW("invalid alert: threshold=%f num_buckets= %d", alert.trigger_if_sum_gt(), - alert.num_buckets()); - return nullopt; - } - const int metricIndex = itr->second; - sp<MetricProducer> metric = allMetricProducers[metricIndex]; - sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor); - if (anomalyTracker == nullptr) { - // The ALOGW for this invalid alert was already displayed in addAnomalyTracker(). - return nullopt; - } - return {anomalyTracker}; -} - -bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - unordered_map<int64_t, int>& atomMatchingTrackerMap, - vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - set<int>& allTagIds) { - vector<AtomMatcher> matcherConfigs; - const int atomMatcherCount = config.atom_matcher_size(); - matcherConfigs.reserve(atomMatcherCount); - allAtomMatchingTrackers.reserve(atomMatcherCount); - - for (int i = 0; i < atomMatcherCount; i++) { - const AtomMatcher& logMatcher = config.atom_matcher(i); - sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, i, uidMap); - if (tracker == nullptr) { - return false; - } - allAtomMatchingTrackers.push_back(tracker); - if (atomMatchingTrackerMap.find(logMatcher.id()) != atomMatchingTrackerMap.end()) { - ALOGE("Duplicate AtomMatcher found!"); - return false; - } - atomMatchingTrackerMap[logMatcher.id()] = i; - matcherConfigs.push_back(logMatcher); - } - - vector<bool> stackTracker2(allAtomMatchingTrackers.size(), false); - for (auto& matcher : allAtomMatchingTrackers) { - if (!matcher->init(matcherConfigs, allAtomMatchingTrackers, atomMatchingTrackerMap, - stackTracker2)) { - return false; - } - // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only. - const set<int>& tagIds = matcher->getAtomIds(); - allTagIds.insert(tagIds.begin(), tagIds.end()); - } - return true; -} - -bool initConditions(const ConfigKey& key, const StatsdConfig& config, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - unordered_map<int64_t, int>& conditionTrackerMap, - vector<sp<ConditionTracker>>& allConditionTrackers, - unordered_map<int, std::vector<int>>& trackerToConditionMap, - vector<ConditionState>& initialConditionCache) { - vector<Predicate> conditionConfigs; - const int conditionTrackerCount = config.predicate_size(); - conditionConfigs.reserve(conditionTrackerCount); - allConditionTrackers.reserve(conditionTrackerCount); - initialConditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated); - - for (int i = 0; i < conditionTrackerCount; i++) { - const Predicate& condition = config.predicate(i); - sp<ConditionTracker> tracker = - createConditionTracker(key, condition, i, atomMatchingTrackerMap); - if (tracker == nullptr) { - return false; - } - allConditionTrackers.push_back(tracker); - if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) { - ALOGE("Duplicate Predicate found!"); - return false; - } - conditionTrackerMap[condition.id()] = i; - conditionConfigs.push_back(condition); - } - - vector<bool> stackTracker(allConditionTrackers.size(), false); - for (size_t i = 0; i < allConditionTrackers.size(); i++) { - auto& conditionTracker = allConditionTrackers[i]; - if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap, - stackTracker, initialConditionCache)) { - return false; - } - for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) { - auto& conditionList = trackerToConditionMap[trackerIndex]; - conditionList.push_back(i); - } - } - return true; -} - -bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap, - unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - map<int64_t, uint64_t>& stateProtoHashes) { - for (int i = 0; i < config.state_size(); i++) { - const State& state = config.state(i); - const int64_t stateId = state.id(); - stateAtomIdMap[stateId] = state.atom_id(); - - string serializedState; - if (!state.SerializeToString(&serializedState)) { - ALOGE("Unable to serialize state %lld", (long long)stateId); - return false; - } - stateProtoHashes[stateId] = Hash64(serializedState); - - const StateMap& stateMap = state.map(); - for (auto group : stateMap.group()) { - for (auto value : group.value()) { - allStateGroupMaps[stateId][value] = group.group_id(); - } - } - } - - return true; -} - -bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, - const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - const unordered_map<int64_t, int>& conditionTrackerMap, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& stateAtomIdMap, - const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - vector<sp<ConditionTracker>>& allConditionTrackers, - const vector<ConditionState>& initialConditionCache, - vector<sp<MetricProducer>>& allMetricProducers, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers); - sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers); - const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + - config.event_metric_size() + config.gauge_metric_size() + - config.value_metric_size(); - allMetricProducers.reserve(allMetricsCount); - - // Construct map from metric id to metric activation index. The map will be used to determine - // the metric activation corresponding to a metric. - unordered_map<int64_t, int> metricToActivationMap; - for (int i = 0; i < config.metric_activation_size(); i++) { - const MetricActivation& metricActivation = config.metric_activation(i); - int64_t metricId = metricActivation.metric_id(); - if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) { - ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId); - return false; - } - metricToActivationMap.insert({metricId, i}); - } - - // Build MetricProducers for each metric defined in config. - // build CountMetricProducer - for (int i = 0; i < config.count_metric_size(); i++) { - int metricIndex = allMetricProducers.size(); - const CountMetric& metric = config.count_metric(i); - metricMap.insert({metric.id(), metricIndex}); - optional<sp<MetricProducer>> producer = createCountMetricProducerAndUpdateMetadata( - key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex, - allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers, - conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, - allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); - if (!producer) { - return false; - } - allMetricProducers.push_back(producer.value()); - } - - // build DurationMetricProducer - for (int i = 0; i < config.duration_metric_size(); i++) { - int metricIndex = allMetricProducers.size(); - const DurationMetric& metric = config.duration_metric(i); - metricMap.insert({metric.id(), metricIndex}); - - optional<sp<MetricProducer>> producer = createDurationMetricProducerAndUpdateMetadata( - key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex, - allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers, - conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, - allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); - if (!producer) { - return false; - } - allMetricProducers.push_back(producer.value()); - } - - // build EventMetricProducer - for (int i = 0; i < config.event_metric_size(); i++) { - int metricIndex = allMetricProducers.size(); - const EventMetric& metric = config.event_metric(i); - metricMap.insert({metric.id(), metricIndex}); - optional<sp<MetricProducer>> producer = createEventMetricProducerAndUpdateMetadata( - key, config, timeBaseTimeNs, metric, metricIndex, allAtomMatchingTrackers, - atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, - initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap, - conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); - if (!producer) { - return false; - } - allMetricProducers.push_back(producer.value()); - } - - // build ValueMetricProducer - for (int i = 0; i < config.value_metric_size(); i++) { - int metricIndex = allMetricProducers.size(); - const ValueMetric& metric = config.value_metric(i); - metricMap.insert({metric.id(), metricIndex}); - optional<sp<MetricProducer>> producer = createValueMetricProducerAndUpdateMetadata( - key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex, - allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers, - conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap, - allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); - if (!producer) { - return false; - } - allMetricProducers.push_back(producer.value()); - } - - // Gauge metrics. - for (int i = 0; i < config.gauge_metric_size(); i++) { - int metricIndex = allMetricProducers.size(); - const GaugeMetric& metric = config.gauge_metric(i); - metricMap.insert({metric.id(), metricIndex}); - optional<sp<MetricProducer>> producer = createGaugeMetricProducerAndUpdateMetadata( - key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex, - allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers, - conditionTrackerMap, initialConditionCache, wizard, matcherWizard, - metricToActivationMap, trackerToMetricMap, conditionToMetricMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); - if (!producer) { - return false; - } - allMetricProducers.push_back(producer.value()); - } - for (int i = 0; i < config.no_report_metric_size(); ++i) { - const auto no_report_metric = config.no_report_metric(i); - if (metricMap.find(no_report_metric) == metricMap.end()) { - ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric); - return false; - } - noReportMetricIds.insert(no_report_metric); - } - - const set<int> whitelistedAtomIds(config.whitelisted_atom_ids().begin(), - config.whitelisted_atom_ids().end()); - for (const auto& it : allMetricProducers) { - // Register metrics to StateTrackers - for (int atomId : it->getSlicedStateAtoms()) { - // Register listener for non-whitelisted atoms only. Using whitelisted atom as a sliced - // state atom is not allowed. - if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) { - StateManager::getInstance().registerListener(atomId, it); - } else { - return false; - } - } - } - return true; -} - -bool initAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap, - unordered_map<int64_t, int>& alertTrackerMap, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - vector<sp<MetricProducer>>& allMetricProducers, - vector<sp<AnomalyTracker>>& allAnomalyTrackers) { - for (int i = 0; i < config.alert_size(); i++) { - const Alert& alert = config.alert(i); - alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); - optional<sp<AnomalyTracker>> anomalyTracker = createAnomalyTracker( - alert, anomalyAlarmMonitor, metricProducerMap, allMetricProducers); - if (!anomalyTracker) { - return false; - } - allAnomalyTrackers.push_back(anomalyTracker.value()); - } - if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, alertTrackerMap, - allAnomalyTrackers)) { - return false; - } - return true; -} - -bool initAlarms(const StatsdConfig& config, const ConfigKey& key, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, vector<sp<AlarmTracker>>& allAlarmTrackers) { - unordered_map<int64_t, int> alarmTrackerMap; - int64_t startMillis = timeBaseNs / 1000 / 1000; - int64_t currentTimeMillis = currentTimeNs / 1000 / 1000; - for (int i = 0; i < config.alarm_size(); i++) { - const Alarm& alarm = config.alarm(i); - if (alarm.offset_millis() <= 0) { - ALOGW("Alarm offset_millis should be larger than 0."); - return false; - } - if (alarm.period_millis() <= 0) { - ALOGW("Alarm period_millis should be larger than 0."); - return false; - } - alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size())); - allAlarmTrackers.push_back( - new AlarmTracker(startMillis, currentTimeMillis, alarm, key, periodicAlarmMonitor)); - } - if (!initSubscribersForSubscriptionType(config, Subscription::ALARM, alarmTrackerMap, - allAlarmTrackers)) { - return false; - } - return true; -} - -bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, set<int>& allTagIds, - vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - unordered_map<int64_t, int>& atomMatchingTrackerMap, - vector<sp<ConditionTracker>>& allConditionTrackers, - unordered_map<int64_t, int>& conditionTrackerMap, - vector<sp<MetricProducer>>& allMetricProducers, - unordered_map<int64_t, int>& metricProducerMap, - vector<sp<AnomalyTracker>>& allAnomalyTrackers, - vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers, - unordered_map<int, std::vector<int>>& conditionToMetricMap, - unordered_map<int, std::vector<int>>& trackerToMetricMap, - unordered_map<int, std::vector<int>>& trackerToConditionMap, - unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - unordered_map<int64_t, int>& alertTrackerMap, - vector<int>& metricsWithActivation, map<int64_t, uint64_t>& stateProtoHashes, - set<int64_t>& noReportMetricIds) { - vector<ConditionState> initialConditionCache; - unordered_map<int64_t, int> stateAtomIdMap; - unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; - - if (!initAtomMatchingTrackers(config, uidMap, atomMatchingTrackerMap, allAtomMatchingTrackers, - allTagIds)) { - ALOGE("initAtomMatchingTrackers failed"); - return false; - } - VLOG("initAtomMatchingTrackers succeed..."); - - if (!initConditions(key, config, atomMatchingTrackerMap, conditionTrackerMap, - allConditionTrackers, trackerToConditionMap, initialConditionCache)) { - ALOGE("initConditionTrackers failed"); - return false; - } - - if (!initStates(config, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)) { - ALOGE("initStates failed"); - return false; - } - if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, atomMatchingTrackerMap, - conditionTrackerMap, allAtomMatchingTrackers, stateAtomIdMap, - allStateGroupMaps, allConditionTrackers, initialConditionCache, - allMetricProducers, conditionToMetricMap, trackerToMetricMap, - metricProducerMap, noReportMetricIds, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - ALOGE("initMetricProducers failed"); - return false; - } - if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor, - allMetricProducers, allAnomalyTrackers)) { - ALOGE("initAlerts failed"); - return false; - } - if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, - allPeriodicAlarmTrackers)) { - ALOGE("initAlarms failed"); - return false; - } - - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h deleted file mode 100644 index 84e1e4e04339..000000000000 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h +++ /dev/null @@ -1,342 +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. - */ - -#pragma once - -#include <set> -#include <unordered_map> -#include <vector> - -#include "anomaly/AlarmTracker.h" -#include "condition/ConditionTracker.h" -#include "external/StatsPullerManager.h" -#include "matchers/AtomMatchingTracker.h" -#include "metrics/MetricProducer.h" - -namespace android { -namespace os { -namespace statsd { - -// Helper functions for creating, validating, and updating config components from StatsdConfig. -// Should only be called from metrics_manager_util and config_update_utils. - -// Create a AtomMatchingTracker. -// input: -// [logMatcher]: the input AtomMatcher from the StatsdConfig -// [index]: the index of the matcher -// output: -// new AtomMatchingTracker, or null if the tracker is unable to be created -sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index, - const sp<UidMap>& uidMap); - -// Create a ConditionTracker. -// input: -// [predicate]: the input Predicate from the StatsdConfig -// [index]: the index of the condition tracker -// [atomMatchingTrackerMap]: map of atom matcher id to its index in allAtomMatchingTrackers -// output: -// new ConditionTracker, or null if the tracker is unable to be created -sp<ConditionTracker> createConditionTracker( - const ConfigKey& key, const Predicate& predicate, const int index, - const unordered_map<int64_t, int>& atomMatchingTrackerMap); - -// Get the hash of a metric, combining the activation if the metric has one. -bool getMetricProtoHash(const StatsdConfig& config, const google::protobuf::MessageLite& metric, - const int64_t id, - const std::unordered_map<int64_t, int>& metricToActivationMap, - uint64_t& metricHash); - -// 1. Validates matcher existence -// 2. Enforces matchers with dimensions and those used for trigger_event are about one atom -// 3. Gets matcher index and updates tracker to metric map -bool handleMetricWithAtomMatchingTrackers( - const int64_t matcherId, const int metricIndex, const bool enforceOneAtom, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex); - -// 1. Validates condition existence, including those in links -// 2. Gets condition index and updates condition to metric map -bool handleMetricWithConditions( - const int64_t condition, const int metricIndex, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>& - links, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap); - -// Validates a metricActivation and populates state. -// Fills the new event activation/deactivation maps, preserving the existing activations. -// Returns false if there are errors. -bool handleMetricActivationOnConfigUpdate( - const StatsdConfig& config, const int64_t metricId, const int metricIndex, - const std::unordered_map<int64_t, int>& metricToActivationMap, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const std::unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation, - std::unordered_map<int, shared_ptr<Activation>>& newEventActivationMap, - std::unordered_map<int, std::vector<shared_ptr<Activation>>>& newEventDeactivationMap); - -// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with -// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. -optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const std::unordered_map<int64_t, int>& stateAtomIdMap, - const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); - -// Creates a DurationMetricProducer and updates the vectors/maps used by MetricsManager with -// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. -optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const std::unordered_map<int64_t, int>& stateAtomIdMap, - const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); - -// Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with -// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. -optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const EventMetric& metric, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); - -// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with -// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. -optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const ValueMetric& metric, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const sp<EventMatcherWizard>& matcherWizard, - const std::unordered_map<int64_t, int>& stateAtomIdMap, - const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); - -// Creates a GaugeMetricProducer and updates the vectors/maps used by MetricsManager with -// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. -optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const GaugeMetric& metric, const int metricIndex, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const sp<EventMatcherWizard>& matcherWizard, - const std::unordered_map<int64_t, int>& metricToActivationMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); - -// Creates an AnomalyTracker and adds it to the appropriate metric. -// Returns an sp to the AnomalyTracker, or nullopt if there was an error. -optional<sp<AnomalyTracker>> createAnomalyTracker( - const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor, - const std::unordered_map<int64_t, int>& metricProducerMap, - std::vector<sp<MetricProducer>>& allMetricProducers); - -// Templated function for adding subscriptions to alarms or alerts. Returns true if successful. -template <typename T> -bool initSubscribersForSubscriptionType(const StatsdConfig& config, - const Subscription_RuleType ruleType, - const std::unordered_map<int64_t, int>& ruleMap, - std::vector<T>& allRules) { - for (int i = 0; i < config.subscription_size(); ++i) { - const Subscription& subscription = config.subscription(i); - if (subscription.rule_type() != ruleType) { - continue; - } - if (subscription.subscriber_information_case() == - Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) { - ALOGW("subscription \"%lld\" has no subscriber info.\"", (long long)subscription.id()); - return false; - } - const auto& itr = ruleMap.find(subscription.rule_id()); - if (itr == ruleMap.end()) { - ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"", - (long long)subscription.id(), (long long)subscription.rule_id()); - return false; - } - const int ruleIndex = itr->second; - allRules[ruleIndex]->addSubscription(subscription); - } - return true; -} - -// Helper functions for MetricsManager to initialize from StatsdConfig. -// *Note*: only initStatsdConfig() should be called from outside. -// All other functions are intermediate -// steps, created to make unit tests easier. And most of the parameters in these -// functions are temporary objects in the initialization phase. - -// Initialize the AtomMatchingTrackers. -// input: -// [key]: the config key that this config belongs to -// [config]: the input StatsdConfig -// output: -// [atomMatchingTrackerMap]: this map should contain matcher name to index mapping -// [allAtomMatchingTrackers]: should store the sp to all the AtomMatchingTracker -// [allTagIds]: contains the set of all interesting tag ids to this config. -bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - std::set<int>& allTagIds); - -// Initialize ConditionTrackers -// input: -// [key]: the config key that this config belongs to -// [config]: the input config -// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step. -// output: -// [conditionTrackerMap]: this map should contain condition name to index mapping -// [allConditionTrackers]: stores the sp to all the ConditionTrackers -// [trackerToConditionMap]: contain the mapping from index of -// log tracker to condition trackers that use the log tracker -// [initialConditionCache]: stores the initial conditions for each ConditionTracker -bool initConditions(const ConfigKey& key, const StatsdConfig& config, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::unordered_map<int64_t, int>& conditionTrackerMap, - std::vector<sp<ConditionTracker>>& allConditionTrackers, - std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - std::vector<ConditionState>& initialConditionCache); - -// Initialize State maps using State protos in the config. These maps will -// eventually be passed to MetricProducers to initialize their state info. -// input: -// [config]: the input config -// output: -// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids -// [allStateGroupMaps]: this map should contain the mapping from states ids and state -// values to state group ids for all states -// [stateProtoHashes]: contains a map of state id to the hash of the State proto from the config -bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap, - unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - std::map<int64_t, uint64_t>& stateProtoHashes); - -// Initialize MetricProducers. -// input: -// [key]: the config key that this config belongs to -// [config]: the input config -// [timeBaseSec]: start time base for all metrics -// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step. -// [conditionTrackerMap]: condition name to index mapping -// [stateAtomIdMap]: contains the mapping from state ids to atom ids -// [allStateGroupMaps]: contains the mapping from atom ids and state values to -// state group ids for all states -// output: -// [allMetricProducers]: contains the list of sp to the MetricProducers created. -// [conditionToMetricMap]: contains the mapping from condition tracker index to -// the list of MetricProducer index -// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index. -bool initMetrics( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, - const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& conditionTrackerMap, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& stateAtomIdMap, - const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - vector<sp<ConditionTracker>>& allConditionTrackers, - const std::vector<ConditionState>& initialConditionCache, - std::vector<sp<MetricProducer>>& allMetricProducers, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::set<int64_t>& noReportMetricIds, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); - -// Initialize alarms -// Is called both on initialize new configs and config updates since alarms do not have any state. -bool initAlarms(const StatsdConfig& config, const ConfigKey& key, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, std::vector<sp<AlarmTracker>>& allAlarmTrackers); - -// Initialize MetricsManager from StatsdConfig. -// Parameters are the members of MetricsManager. See MetricsManager for declaration. -bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, std::set<int>& allTagIds, - std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::vector<sp<ConditionTracker>>& allConditionTrackers, - std::unordered_map<int64_t, int>& conditionTrackerMap, - std::vector<sp<MetricProducer>>& allMetricProducers, - std::unordered_map<int64_t, int>& metricProducerMap, - vector<sp<AnomalyTracker>>& allAnomalyTrackers, - vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::unordered_map<int64_t, int>& alertTrackerMap, - std::vector<int>& metricsWithActivation, - std::map<int64_t, uint64_t>& stateProtoHashes, - std::set<int64_t>& noReportMetricIds); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h deleted file mode 100644 index 1bc84c5433f9..000000000000 --- a/cmds/statsd/src/packages/PackageInfoListener.h +++ /dev/null @@ -1,47 +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. - */ - -#ifndef STATSD_PACKAGE_INFO_LISTENER_H -#define STATSD_PACKAGE_INFO_LISTENER_H - -#include <utils/RefBase.h> - -#include <string> - -namespace android { -namespace os { -namespace statsd { - -class PackageInfoListener : public virtual android::RefBase { -public: - // Uid map will notify this listener that the app with apk name and uid has been upgraded to - // the specified version. - virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const std::string& apk, - const int uid, const int64_t version) = 0; - - // Notify interested listeners that the given apk and uid combination no longer exits. - virtual void notifyAppRemoved(const int64_t& eventTimeNs, const std::string& apk, - const int uid) = 0; - - // Notify the listener that the UidMap snapshot is available. - virtual void onUidMapReceived(const int64_t& eventTimeNs) = 0; -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // STATSD_PACKAGE_INFO_LISTENER_H diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp deleted file mode 100644 index acf40c88a00d..000000000000 --- a/cmds/statsd/src/packages/UidMap.cpp +++ /dev/null @@ -1,563 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, versionCode 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. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "hash.h" -#include "stats_log_util.h" -#include "guardrail/StatsdStats.h" -#include "packages/UidMap.h" - -#include <inttypes.h> - -using namespace android; - -using android::base::StringPrintf; -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_UINT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; - -namespace android { -namespace os { -namespace statsd { - -const int FIELD_ID_SNAPSHOT_PACKAGE_NAME = 1; -const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION = 2; -const int FIELD_ID_SNAPSHOT_PACKAGE_UID = 3; -const int FIELD_ID_SNAPSHOT_PACKAGE_DELETED = 4; -const int FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH = 5; -const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING = 6; -const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH = 7; -const int FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER = 8; -const int FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH = 9; -const int FIELD_ID_SNAPSHOT_TIMESTAMP = 1; -const int FIELD_ID_SNAPSHOT_PACKAGE_INFO = 2; -const int FIELD_ID_SNAPSHOTS = 1; -const int FIELD_ID_CHANGES = 2; -const int FIELD_ID_CHANGE_DELETION = 1; -const int FIELD_ID_CHANGE_TIMESTAMP = 2; -const int FIELD_ID_CHANGE_PACKAGE = 3; -const int FIELD_ID_CHANGE_UID = 4; -const int FIELD_ID_CHANGE_NEW_VERSION = 5; -const int FIELD_ID_CHANGE_PREV_VERSION = 6; -const int FIELD_ID_CHANGE_PACKAGE_HASH = 7; -const int FIELD_ID_CHANGE_NEW_VERSION_STRING = 8; -const int FIELD_ID_CHANGE_PREV_VERSION_STRING = 9; -const int FIELD_ID_CHANGE_NEW_VERSION_STRING_HASH = 10; -const int FIELD_ID_CHANGE_PREV_VERSION_STRING_HASH = 11; - -UidMap::UidMap() : mBytesUsed(0) {} - -UidMap::~UidMap() {} - -sp<UidMap> UidMap::getInstance() { - static sp<UidMap> sInstance = new UidMap(); - return sInstance; -} - -bool UidMap::hasApp(int uid, const string& packageName) const { - lock_guard<mutex> lock(mMutex); - - auto it = mMap.find(std::make_pair(uid, packageName)); - return it != mMap.end() && !it->second.deleted; -} - -string UidMap::normalizeAppName(const string& appName) const { - string normalizedName = appName; - std::transform(normalizedName.begin(), normalizedName.end(), normalizedName.begin(), ::tolower); - return normalizedName; -} - -std::set<string> UidMap::getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const { - lock_guard<mutex> lock(mMutex); - return getAppNamesFromUidLocked(uid,returnNormalized); -} - -std::set<string> UidMap::getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const { - std::set<string> names; - for (const auto& kv : mMap) { - if (kv.first.first == uid && !kv.second.deleted) { - names.insert(returnNormalized ? normalizeAppName(kv.first.second) : kv.first.second); - } - } - return names; -} - -int64_t UidMap::getAppVersion(int uid, const string& packageName) const { - lock_guard<mutex> lock(mMutex); - - auto it = mMap.find(std::make_pair(uid, packageName)); - if (it == mMap.end() || it->second.deleted) { - return 0; - } - return it->second.versionCode; -} - -void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, - const vector<int64_t>& versionCode, const vector<String16>& versionString, - const vector<String16>& packageName, const vector<String16>& installer) { - wp<PackageInfoListener> broadcast = NULL; - { - lock_guard<mutex> lock(mMutex); // Exclusively lock for updates. - - std::unordered_map<std::pair<int, string>, AppData, PairHash> deletedApps; - - // Copy all the deleted apps. - for (const auto& kv : mMap) { - if (kv.second.deleted) { - deletedApps[kv.first] = kv.second; - } - } - - mMap.clear(); - for (size_t j = 0; j < uid.size(); j++) { - string package = string(String8(packageName[j]).string()); - mMap[std::make_pair(uid[j], package)] = - AppData(versionCode[j], string(String8(versionString[j]).string()), - string(String8(installer[j]).string())); - } - - for (const auto& kv : deletedApps) { - auto mMapIt = mMap.find(kv.first); - if (mMapIt != mMap.end()) { - // Insert this deleted app back into the current map. - mMap[kv.first] = kv.second; - } - } - - ensureBytesUsedBelowLimit(); - StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); - broadcast = mSubscriber; - } - // To avoid invoking callback while holding the internal lock. we get a copy of the listener - // and invoke the callback. It's still possible that after we copy the listener, it removes - // itself before we call it. It's then the listener's job to handle it (expect the callback to - // be called after listener is removed, and the listener should properly ignore it). - auto strongPtr = broadcast.promote(); - if (strongPtr != NULL) { - strongPtr->onUidMapReceived(timestamp); - } -} - -void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid, - const int64_t& versionCode, const String16& versionString, - const String16& installer) { - wp<PackageInfoListener> broadcast = NULL; - string appName = string(String8(app_16).string()); - { - lock_guard<mutex> lock(mMutex); - int32_t prevVersion = 0; - string prevVersionString = ""; - string newVersionString = string(String8(versionString).string()); - bool found = false; - auto it = mMap.find(std::make_pair(uid, appName)); - if (it != mMap.end()) { - found = true; - prevVersion = it->second.versionCode; - prevVersionString = it->second.versionString; - it->second.versionCode = versionCode; - it->second.versionString = newVersionString; - it->second.installer = string(String8(installer).string()); - it->second.deleted = false; - } - if (!found) { - // Otherwise, we need to add an app at this uid. - mMap[std::make_pair(uid, appName)] = - AppData(versionCode, newVersionString, string(String8(installer).string())); - } else { - // Only notify the listeners if this is an app upgrade. If this app is being installed - // for the first time, then we don't notify the listeners. - // It's also OK to split again if we're forming a partial bucket after re-installing an - // app after deletion. - broadcast = mSubscriber; - } - mChanges.emplace_back(false, timestamp, appName, uid, versionCode, newVersionString, - prevVersion, prevVersionString); - mBytesUsed += kBytesChangeRecord; - ensureBytesUsedBelowLimit(); - StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); - StatsdStats::getInstance().setUidMapChanges(mChanges.size()); - } - - auto strongPtr = broadcast.promote(); - if (strongPtr != NULL) { - strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode); - } -} - -void UidMap::ensureBytesUsedBelowLimit() { - size_t limit; - if (maxBytesOverride <= 0) { - limit = StatsdStats::kMaxBytesUsedUidMap; - } else { - limit = maxBytesOverride; - } - while (mBytesUsed > limit) { - ALOGI("Bytes used %zu is above limit %zu, need to delete something", mBytesUsed, limit); - if (mChanges.size() > 0) { - mBytesUsed -= kBytesChangeRecord; - mChanges.pop_front(); - StatsdStats::getInstance().noteUidMapDropped(1); - } - } -} - -void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) { - wp<PackageInfoListener> broadcast = NULL; - string app = string(String8(app_16).string()); - { - lock_guard<mutex> lock(mMutex); - - int64_t prevVersion = 0; - string prevVersionString = ""; - auto key = std::make_pair(uid, app); - auto it = mMap.find(key); - if (it != mMap.end() && !it->second.deleted) { - prevVersion = it->second.versionCode; - prevVersionString = it->second.versionString; - it->second.deleted = true; - mDeletedApps.push_back(key); - } - if (mDeletedApps.size() > StatsdStats::kMaxDeletedAppsInUidMap) { - // Delete the oldest one. - auto oldest = mDeletedApps.front(); - mDeletedApps.pop_front(); - mMap.erase(oldest); - StatsdStats::getInstance().noteUidMapAppDeletionDropped(); - } - mChanges.emplace_back(true, timestamp, app, uid, 0, "", prevVersion, prevVersionString); - mBytesUsed += kBytesChangeRecord; - ensureBytesUsedBelowLimit(); - StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); - StatsdStats::getInstance().setUidMapChanges(mChanges.size()); - broadcast = mSubscriber; - } - - auto strongPtr = broadcast.promote(); - if (strongPtr != NULL) { - strongPtr->notifyAppRemoved(timestamp, app, uid); - } -} - -void UidMap::setListener(wp<PackageInfoListener> listener) { - lock_guard<mutex> lock(mMutex); // Lock for updates - mSubscriber = listener; -} - -void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) { - lock_guard<mutex> lock(mIsolatedMutex); - - mIsolatedUidMap[isolatedUid] = parentUid; -} - -void UidMap::removeIsolatedUid(int isolatedUid) { - lock_guard<mutex> lock(mIsolatedMutex); - - auto it = mIsolatedUidMap.find(isolatedUid); - if (it != mIsolatedUidMap.end()) { - mIsolatedUidMap.erase(it); - } -} - -int UidMap::getHostUidOrSelf(int uid) const { - lock_guard<mutex> lock(mIsolatedMutex); - - auto it = mIsolatedUidMap.find(uid); - if (it != mIsolatedUidMap.end()) { - return it->second; - } - return uid; -} - -void UidMap::clearOutput() { - mChanges.clear(); - // Also update the guardrail trackers. - StatsdStats::getInstance().setUidMapChanges(0); - mBytesUsed = 0; - StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); -} - -int64_t UidMap::getMinimumTimestampNs() { - int64_t m = 0; - for (const auto& kv : mLastUpdatePerConfigKey) { - if (m == 0) { - m = kv.second; - } else if (kv.second < m) { - m = kv.second; - } - } - return m; -} - -size_t UidMap::getBytesUsed() const { - return mBytesUsed; -} - -void UidMap::writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, - bool includeInstaller, const std::set<int32_t>& interestingUids, - std::set<string>* str_set, ProtoOutputStream* proto) { - lock_guard<mutex> lock(mMutex); - - writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, interestingUids, - str_set, proto); -} - -void UidMap::writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings, - bool includeInstaller, - const std::set<int32_t>& interestingUids, - std::set<string>* str_set, ProtoOutputStream* proto) { - proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp); - for (const auto& kv : mMap) { - if (!interestingUids.empty() && - interestingUids.find(kv.first.first) == interestingUids.end()) { - continue; - } - uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SNAPSHOT_PACKAGE_INFO); - if (str_set != nullptr) { - str_set->insert(kv.first.second); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH, - (long long)Hash64(kv.first.second)); - if (includeVersionStrings) { - str_set->insert(kv.second.versionString); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH, - (long long)Hash64(kv.second.versionString)); - } - if (includeInstaller) { - str_set->insert(kv.second.installer); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH, - (long long)Hash64(kv.second.installer)); - } - } else { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second); - if (includeVersionStrings) { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING, - kv.second.versionString); - } - if (includeInstaller) { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER, - kv.second.installer); - } - } - - proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, - (long long)kv.second.versionCode); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first); - proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted); - proto->end(token); - } -} - -void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set, - bool includeVersionStrings, bool includeInstaller, - ProtoOutputStream* proto) { - lock_guard<mutex> lock(mMutex); // Lock for updates - - for (const ChangeRecord& record : mChanges) { - if (record.timestampNs > mLastUpdatePerConfigKey[key]) { - uint64_t changesToken = - proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CHANGES); - proto->write(FIELD_TYPE_BOOL | FIELD_ID_CHANGE_DELETION, (bool)record.deletion); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_TIMESTAMP, - (long long)record.timestampNs); - if (str_set != nullptr) { - str_set->insert(record.package); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PACKAGE_HASH, - (long long)Hash64(record.package)); - if (includeVersionStrings) { - str_set->insert(record.versionString); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_NEW_VERSION_STRING_HASH, - (long long)Hash64(record.versionString)); - str_set->insert(record.prevVersionString); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PREV_VERSION_STRING_HASH, - (long long)Hash64(record.prevVersionString)); - } - } else { - proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package); - if (includeVersionStrings) { - proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_NEW_VERSION_STRING, - record.versionString); - proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PREV_VERSION_STRING, - record.prevVersionString); - } - } - - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_NEW_VERSION, (long long)record.version); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_PREV_VERSION, - (long long)record.prevVersion); - proto->end(changesToken); - } - } - - // Write snapshot from current uid map state. - uint64_t snapshotsToken = - proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS); - writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, - std::set<int32_t>() /*empty uid set means including every uid*/, - str_set, proto); - proto->end(snapshotsToken); - - int64_t prevMin = getMinimumTimestampNs(); - mLastUpdatePerConfigKey[key] = timestamp; - int64_t newMin = getMinimumTimestampNs(); - - if (newMin > prevMin) { // Delete anything possible now that the minimum has - // moved forward. - int64_t cutoff_nanos = newMin; - for (auto it_changes = mChanges.begin(); it_changes != mChanges.end();) { - if (it_changes->timestampNs < cutoff_nanos) { - mBytesUsed -= kBytesChangeRecord; - it_changes = mChanges.erase(it_changes); - } else { - ++it_changes; - } - } - } - StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); - StatsdStats::getInstance().setUidMapChanges(mChanges.size()); -} - -void UidMap::printUidMap(int out) const { - lock_guard<mutex> lock(mMutex); - - for (const auto& kv : mMap) { - if (!kv.second.deleted) { - dprintf(out, "%s, v%" PRId64 ", %s, %s (%i)\n", kv.first.second.c_str(), - kv.second.versionCode, kv.second.versionString.c_str(), - kv.second.installer.c_str(), kv.first.first); - } - } -} - -void UidMap::OnConfigUpdated(const ConfigKey& key) { - mLastUpdatePerConfigKey[key] = -1; -} - -void UidMap::OnConfigRemoved(const ConfigKey& key) { - mLastUpdatePerConfigKey.erase(key); -} - -set<int32_t> UidMap::getAppUid(const string& package) const { - lock_guard<mutex> lock(mMutex); - - set<int32_t> results; - for (const auto& kv : mMap) { - if (kv.first.second == package && !kv.second.deleted) { - results.insert(kv.first.first); - } - } - return results; -} - -// Note not all the following AIDs are used as uids. Some are used only for gids. -// It's ok to leave them in the map, but we won't ever see them in the log's uid field. -// App's uid starts from 10000, and will not overlap with the following AIDs. -const std::map<string, uint32_t> UidMap::sAidToUidMapping = {{"AID_ROOT", 0}, - {"AID_SYSTEM", 1000}, - {"AID_RADIO", 1001}, - {"AID_BLUETOOTH", 1002}, - {"AID_GRAPHICS", 1003}, - {"AID_INPUT", 1004}, - {"AID_AUDIO", 1005}, - {"AID_CAMERA", 1006}, - {"AID_LOG", 1007}, - {"AID_COMPASS", 1008}, - {"AID_MOUNT", 1009}, - {"AID_WIFI", 1010}, - {"AID_ADB", 1011}, - {"AID_INSTALL", 1012}, - {"AID_MEDIA", 1013}, - {"AID_DHCP", 1014}, - {"AID_SDCARD_RW", 1015}, - {"AID_VPN", 1016}, - {"AID_KEYSTORE", 1017}, - {"AID_USB", 1018}, - {"AID_DRM", 1019}, - {"AID_MDNSR", 1020}, - {"AID_GPS", 1021}, - // {"AID_UNUSED1", 1022}, - {"AID_MEDIA_RW", 1023}, - {"AID_MTP", 1024}, - // {"AID_UNUSED2", 1025}, - {"AID_DRMRPC", 1026}, - {"AID_NFC", 1027}, - {"AID_SDCARD_R", 1028}, - {"AID_CLAT", 1029}, - {"AID_LOOP_RADIO", 1030}, - {"AID_MEDIA_DRM", 1031}, - {"AID_PACKAGE_INFO", 1032}, - {"AID_SDCARD_PICS", 1033}, - {"AID_SDCARD_AV", 1034}, - {"AID_SDCARD_ALL", 1035}, - {"AID_LOGD", 1036}, - {"AID_SHARED_RELRO", 1037}, - {"AID_DBUS", 1038}, - {"AID_TLSDATE", 1039}, - {"AID_MEDIA_EX", 1040}, - {"AID_AUDIOSERVER", 1041}, - {"AID_METRICS_COLL", 1042}, - {"AID_METRICSD", 1043}, - {"AID_WEBSERV", 1044}, - {"AID_DEBUGGERD", 1045}, - {"AID_MEDIA_CODEC", 1046}, - {"AID_CAMERASERVER", 1047}, - {"AID_FIREWALL", 1048}, - {"AID_TRUNKS", 1049}, - {"AID_NVRAM", 1050}, - {"AID_DNS", 1051}, - {"AID_DNS_TETHER", 1052}, - {"AID_WEBVIEW_ZYGOTE", 1053}, - {"AID_VEHICLE_NETWORK", 1054}, - {"AID_MEDIA_AUDIO", 1055}, - {"AID_MEDIA_VIDEO", 1056}, - {"AID_MEDIA_IMAGE", 1057}, - {"AID_TOMBSTONED", 1058}, - {"AID_MEDIA_OBB", 1059}, - {"AID_ESE", 1060}, - {"AID_OTA_UPDATE", 1061}, - {"AID_AUTOMOTIVE_EVS", 1062}, - {"AID_LOWPAN", 1063}, - {"AID_HSM", 1064}, - {"AID_RESERVED_DISK", 1065}, - {"AID_STATSD", 1066}, - {"AID_INCIDENTD", 1067}, - {"AID_SECURE_ELEMENT", 1068}, - {"AID_LMKD", 1069}, - {"AID_LLKD", 1070}, - {"AID_IORAPD", 1071}, - {"AID_GPU_SERVICE", 1072}, - {"AID_NETWORK_STACK", 1073}, - {"AID_GSID", 1074}, - {"AID_FSVERITY_CERT", 1075}, - {"AID_CREDSTORE", 1076}, - {"AID_EXTERNAL_STORAGE", 1077}, - {"AID_EXT_DATA_RW", 1078}, - {"AID_EXT_OBB_RW", 1079}, - {"AID_CONTEXT_HUB", 1080}, - {"AID_SHELL", 2000}, - {"AID_CACHE", 2001}, - {"AID_DIAG", 2002}}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h deleted file mode 100644 index 622321b804ec..000000000000 --- a/cmds/statsd/src/packages/UidMap.h +++ /dev/null @@ -1,226 +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. - */ - -#pragma once - -#include "config/ConfigKey.h" -#include "packages/PackageInfoListener.h" -#include "stats_util.h" - -#include <gtest/gtest_prod.h> -#include <stdio.h> -#include <utils/RefBase.h> -#include <utils/String16.h> - -#include <list> -#include <mutex> -#include <set> -#include <string> -#include <unordered_map> - -using namespace android; -using namespace std; - -using android::util::ProtoOutputStream; - -namespace android { -namespace os { -namespace statsd { - -struct AppData { - int64_t versionCode; - string versionString; - string installer; - bool deleted; - - // Empty constructor needed for unordered map. - AppData() { - } - - AppData(const int64_t v, const string& versionString, const string& installer) - : versionCode(v), versionString(versionString), installer(installer), deleted(false){}; -}; - -// When calling appendUidMap, we retrieve all the ChangeRecords since the last -// timestamp we called appendUidMap for this configuration key. -struct ChangeRecord { - const bool deletion; - const int64_t timestampNs; - const string package; - const int32_t uid; - const int64_t version; - const int64_t prevVersion; - const string versionString; - const string prevVersionString; - - ChangeRecord(const bool isDeletion, const int64_t timestampNs, const string& package, - const int32_t uid, const int64_t version, const string versionString, - const int64_t prevVersion, const string prevVersionString) - : deletion(isDeletion), - timestampNs(timestampNs), - package(package), - uid(uid), - version(version), - prevVersion(prevVersion), - versionString(versionString), - prevVersionString(prevVersionString) { - } -}; - -const unsigned int kBytesChangeRecord = sizeof(struct ChangeRecord); - -// UidMap keeps track of what the corresponding app name (APK name) and version code for every uid -// at any given moment. This map must be updated by StatsCompanionService. -class UidMap : public virtual android::RefBase { -public: - UidMap(); - ~UidMap(); - static const std::map<std::string, uint32_t> sAidToUidMapping; - - static sp<UidMap> getInstance(); - /* - * All three inputs must be the same size, and the jth element in each array refers to the same - * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j]. - */ - void updateMap(const int64_t& timestamp, const vector<int32_t>& uid, - const vector<int64_t>& versionCode, const vector<String16>& versionString, - const vector<String16>& packageName, const vector<String16>& installer); - - void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid, - const int64_t& versionCode, const String16& versionString, - const String16& installer); - void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid); - - // Returns true if the given uid contains the specified app (eg. com.google.android.gms). - bool hasApp(int uid, const string& packageName) const; - - // Returns the app names from uid. - std::set<string> getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const; - - int64_t getAppVersion(int uid, const string& packageName) const; - - // Helper for debugging contents of this uid map. Can be triggered with: - // adb shell cmd stats print-uid-map - void printUidMap(int outFd) const; - - // Command for indicating to the map that StatsLogProcessor should be notified if an app is - // updated. This allows metric producers and managers to distinguish when the same uid or app - // represents a different version of an app. - void setListener(wp<PackageInfoListener> listener); - - // Informs uid map that a config is added/updated. Used for keeping mConfigKeys up to date. - void OnConfigUpdated(const ConfigKey& key); - - // Informs uid map that a config is removed. Used for keeping mConfigKeys up to date. - void OnConfigRemoved(const ConfigKey& key); - - void assignIsolatedUid(int isolatedUid, int parentUid); - void removeIsolatedUid(int isolatedUid); - - // Returns the host uid if it exists. Otherwise, returns the same uid that was passed-in. - virtual int getHostUidOrSelf(int uid) const; - - // Gets all snapshots and changes that have occurred since the last output. - // If every config key has received a change or snapshot record, then this - // record is deleted. - void appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set, - bool includeVersionStrings, bool includeInstaller, - 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 - // in case we lose a previous upload. - void clearOutput(); - - // Get currently cached value of memory used by UID map. - size_t getBytesUsed() const; - - virtual std::set<int32_t> getAppUid(const string& package) const; - - // Write current PackageInfoSnapshot to ProtoOutputStream. - // interestingUids: If not empty, only write the package info for these uids. If empty, write - // package info for all uids. - // str_set: if not null, add new string to the set and write str_hash to proto - // if null, write string to proto. - void writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, bool includeInstaller, - const std::set<int32_t>& interestingUids, std::set<string>* str_set, - ProtoOutputStream* proto); - -private: - std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const; - string normalizeAppName(const string& appName) const; - - void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings, - bool includeInstaller, const std::set<int32_t>& interestingUids, - std::set<string>* str_set, ProtoOutputStream* proto); - - mutable mutex mMutex; - mutable mutex mIsolatedMutex; - - struct PairHash { - size_t operator()(std::pair<int, string> p) const noexcept { - std::hash<std::string> hash_fn; - return hash_fn(std::to_string(p.first) + p.second); - } - }; - // Maps uid and package name to application data. - std::unordered_map<std::pair<int, string>, AppData, PairHash> mMap; - - // Maps isolated uid to the parent uid. Any metrics for an isolated uid will instead contribute - // to the parent uid. - std::unordered_map<int, int> mIsolatedUidMap; - - // Record the changes that can be provided with the uploads. - std::list<ChangeRecord> mChanges; - - // Store which uid and apps represent deleted ones. - std::list<std::pair<int, string>> mDeletedApps; - - // Notify StatsLogProcessor if there's an upgrade/removal in any app. - wp<PackageInfoListener> mSubscriber; - - // Mapping of config keys we're aware of to the epoch time they last received an update. This - // lets us know it's safe to delete events older than the oldest update. The value is nanosec. - // Value of -1 denotes this config key has never received an upload. - std::unordered_map<ConfigKey, int64_t> mLastUpdatePerConfigKey; - - // Returns the minimum value from mConfigKeys. - int64_t getMinimumTimestampNs(); - - // If our current used bytes is above the limit, then we clear out the earliest snapshot. If - // there are no more snapshots, then we clear out the earliest delta. We repeat the deletions - // until the memory consumed by mOutput is below the specified limit. - void ensureBytesUsedBelowLimit(); - - // Override used for testing the max memory allowed by uid map. 0 means we use the value - // specified in StatsdStats.h with the rest of the guardrails. - size_t maxBytesOverride = 0; - - // Cache the size of mOutput; - size_t mBytesUsed; - - // Allows unit-test to access private methods. - FRIEND_TEST(UidMapTest, TestClearingOutput); - FRIEND_TEST(UidMapTest, TestRemovedAppRetained); - FRIEND_TEST(UidMapTest, TestRemovedAppOverGuardrail); - FRIEND_TEST(UidMapTest, TestOutputIncludesAtLeastOneSnapshot); - FRIEND_TEST(UidMapTest, TestMemoryComputed); - FRIEND_TEST(UidMapTest, TestMemoryGuardrail); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp deleted file mode 100644 index 9d8f0c24e253..000000000000 --- a/cmds/statsd/src/shell/ShellSubscriber.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* - * 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. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "ShellSubscriber.h" - -#include <android-base/file.h> - -#include "matchers/matcher_util.h" -#include "stats_log_util.h" - -using android::util::ProtoOutputStream; - -namespace android { -namespace os { -namespace statsd { - -const static int FIELD_ID_ATOM = 1; - -void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) { - int myToken = claimToken(); - VLOG("ShellSubscriber: new subscription %d has come in", myToken); - mSubscriptionShouldEnd.notify_one(); - - shared_ptr<SubscriptionInfo> mySubscriptionInfo = make_shared<SubscriptionInfo>(in, out); - if (!readConfig(mySubscriptionInfo)) return; - - { - std::unique_lock<std::mutex> lock(mMutex); - mSubscriptionInfo = mySubscriptionInfo; - spawnHelperThread(myToken); - waitForSubscriptionToEndLocked(mySubscriptionInfo, myToken, lock, timeoutSec); - - if (mSubscriptionInfo == mySubscriptionInfo) { - mSubscriptionInfo = nullptr; - } - - } -} - -void ShellSubscriber::spawnHelperThread(int myToken) { - std::thread t([this, myToken] { pullAndSendHeartbeats(myToken); }); - t.detach(); -} - -void ShellSubscriber::waitForSubscriptionToEndLocked(shared_ptr<SubscriptionInfo> myInfo, - int myToken, - std::unique_lock<std::mutex>& lock, - int timeoutSec) { - if (timeoutSec > 0) { - mSubscriptionShouldEnd.wait_for(lock, timeoutSec * 1s, [this, myToken, &myInfo] { - return mToken != myToken || !myInfo->mClientAlive; - }); - } else { - mSubscriptionShouldEnd.wait(lock, [this, myToken, &myInfo] { - return mToken != myToken || !myInfo->mClientAlive; - }); - } -} - -// Atomically claim the next token. Token numbers denote subscriber ordering. -int ShellSubscriber::claimToken() { - std::unique_lock<std::mutex> lock(mMutex); - int myToken = ++mToken; - return myToken; -} - -// Read and parse single config. There should only one config per input. -bool ShellSubscriber::readConfig(shared_ptr<SubscriptionInfo> subscriptionInfo) { - // Read the size of the config. - size_t bufferSize; - if (!android::base::ReadFully(subscriptionInfo->mInputFd, &bufferSize, sizeof(bufferSize))) { - return false; - } - - // Read the config. - vector<uint8_t> buffer(bufferSize); - if (!android::base::ReadFully(subscriptionInfo->mInputFd, buffer.data(), bufferSize)) { - return false; - } - - // Parse the config. - ShellSubscription config; - if (!config.ParseFromArray(buffer.data(), bufferSize)) { - return false; - } - - // Update SubscriptionInfo with state from config - for (const auto& pushed : config.pushed()) { - subscriptionInfo->mPushedMatchers.push_back(pushed); - } - - for (const auto& pulled : config.pulled()) { - vector<string> packages; - vector<int32_t> uids; - for (const string& pkg : pulled.packages()) { - auto it = UidMap::sAidToUidMapping.find(pkg); - if (it != UidMap::sAidToUidMapping.end()) { - uids.push_back(it->second); - } else { - packages.push_back(pkg); - } - } - - subscriptionInfo->mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis(), packages, - uids); - VLOG("adding matcher for pulled atom %d", pulled.matcher().atom_id()); - } - - return true; -} - -void ShellSubscriber::pullAndSendHeartbeats(int myToken) { - VLOG("ShellSubscriber: helper thread %d starting", myToken); - while (true) { - int64_t sleepTimeMs = INT_MAX; - { - std::lock_guard<std::mutex> lock(mMutex); - if (!mSubscriptionInfo || mToken != myToken) { - VLOG("ShellSubscriber: helper thread %d done!", myToken); - return; - } - - int64_t nowMillis = getElapsedRealtimeMillis(); - int64_t nowNanos = getElapsedRealtimeNs(); - for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) { - if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval >= nowMillis) { - continue; - } - - vector<int32_t> uids; - getUidsForPullAtom(&uids, pullInfo); - - vector<std::shared_ptr<LogEvent>> data; - mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, nowNanos, &data); - VLOG("Pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id()); - writePulledAtomsLocked(data, pullInfo.mPullerMatcher); - - pullInfo.mPrevPullElapsedRealtimeMs = nowMillis; - } - - // Send a heartbeat, consisting of a data size of 0, if perfd hasn't recently received - // data from statsd. When it receives the data size of 0, perfd will not expect any - // atoms and recheck whether the subscription should end. - if (nowMillis - mLastWriteMs > kMsBetweenHeartbeats) { - attemptWriteToPipeLocked(/*dataSize=*/0); - } - - // Determine how long to sleep before doing more work. - for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) { - int64_t nextPullTime = pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval; - int64_t timeBeforePull = nextPullTime - nowMillis; // guaranteed to be non-negative - if (timeBeforePull < sleepTimeMs) sleepTimeMs = timeBeforePull; - } - int64_t timeBeforeHeartbeat = (mLastWriteMs + kMsBetweenHeartbeats) - nowMillis; - if (timeBeforeHeartbeat < sleepTimeMs) sleepTimeMs = timeBeforeHeartbeat; - } - - VLOG("ShellSubscriber: helper thread %d sleeping for %lld ms", myToken, - (long long)sleepTimeMs); - std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs)); - } -} - -void ShellSubscriber::getUidsForPullAtom(vector<int32_t>* uids, const PullInfo& pullInfo) { - uids->insert(uids->end(), pullInfo.mPullUids.begin(), pullInfo.mPullUids.end()); - // This is slow. Consider storing the uids per app and listening to uidmap updates. - for (const string& pkg : pullInfo.mPullPackages) { - set<int32_t> uidsForPkg = mUidMap->getAppUid(pkg); - uids->insert(uids->end(), uidsForPkg.begin(), uidsForPkg.end()); - } - uids->push_back(DEFAULT_PULL_UID); -} - -void ShellSubscriber::writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data, - const SimpleAtomMatcher& matcher) { - mProto.clear(); - int count = 0; - for (const auto& event : data) { - if (matchesSimple(mUidMap, matcher, *event)) { - count++; - uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE | - util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM); - event->ToProto(mProto); - mProto.end(atomToken); - } - } - - if (count > 0) attemptWriteToPipeLocked(mProto.size()); -} - -void ShellSubscriber::onLogEvent(const LogEvent& event) { - std::lock_guard<std::mutex> lock(mMutex); - if (!mSubscriptionInfo) return; - - mProto.clear(); - for (const auto& matcher : mSubscriptionInfo->mPushedMatchers) { - if (matchesSimple(mUidMap, matcher, event)) { - uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE | - util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM); - event.ToProto(mProto); - mProto.end(atomToken); - attemptWriteToPipeLocked(mProto.size()); - } - } -} - -// Tries to write the atom encoded in mProto to the pipe. If the write fails -// because the read end of the pipe has closed, signals to other threads that -// the subscription should end. -void ShellSubscriber::attemptWriteToPipeLocked(size_t dataSize) { - // First, write the payload size. - if (!android::base::WriteFully(mSubscriptionInfo->mOutputFd, &dataSize, sizeof(dataSize))) { - mSubscriptionInfo->mClientAlive = false; - mSubscriptionShouldEnd.notify_one(); - return; - } - - // Then, write the payload if this is not just a heartbeat. - if (dataSize > 0 && !mProto.flush(mSubscriptionInfo->mOutputFd)) { - mSubscriptionInfo->mClientAlive = false; - mSubscriptionShouldEnd.notify_one(); - return; - } - - mLastWriteMs = getElapsedRealtimeMillis(); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h deleted file mode 100644 index 4c05fa7f71c2..000000000000 --- a/cmds/statsd/src/shell/ShellSubscriber.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include <android/util/ProtoOutputStream.h> -#include <private/android_filesystem_config.h> - -#include <condition_variable> -#include <mutex> -#include <thread> - -#include "external/StatsPullerManager.h" -#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "logd/LogEvent.h" -#include "packages/UidMap.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * Handles atoms subscription via shell cmd. - * - * A shell subscription lasts *until shell exits*. Unlike config based clients, a shell client - * communicates with statsd via file descriptors. They can subscribe pushed and pulled atoms. - * The atoms are sent back to the client in real time, as opposed to keeping the data in memory. - * Shell clients do not subscribe aggregated metrics, as they are responsible for doing the - * aggregation after receiving the atom events. - * - * Shell clients pass ShellSubscription in the proto binary format. Clients can update the - * subscription by sending a new subscription. The new subscription would replace the old one. - * Input data stream format is: - * - * |size_t|subscription proto|size_t|subscription proto|.... - * - * statsd sends the events back in Atom proto binary format. Each Atom message is preceded - * with sizeof(size_t) bytes indicating the size of the proto message payload. - * - * The stream would be in the following format: - * |size_t|shellData proto|size_t|shellData proto|.... - * - * Only one shell subscriber is allowed at a time because each shell subscriber blocks one thread - * until it exits. - */ -class ShellSubscriber : public virtual RefBase { -public: - ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr) - : mUidMap(uidMap), mPullerMgr(pullerMgr){}; - - void startNewSubscription(int inFd, int outFd, int timeoutSec); - - void onLogEvent(const LogEvent& event); - -private: - struct PullInfo { - PullInfo(const SimpleAtomMatcher& matcher, int64_t interval, - const std::vector<std::string>& packages, const std::vector<int32_t>& uids) - : mPullerMatcher(matcher), - mInterval(interval), - mPrevPullElapsedRealtimeMs(0), - mPullPackages(packages), - mPullUids(uids) { - } - SimpleAtomMatcher mPullerMatcher; - int64_t mInterval; - int64_t mPrevPullElapsedRealtimeMs; - std::vector<std::string> mPullPackages; - std::vector<int32_t> mPullUids; - }; - - struct SubscriptionInfo { - SubscriptionInfo(const int& inputFd, const int& outputFd) - : mInputFd(inputFd), mOutputFd(outputFd), mClientAlive(true) { - } - - int mInputFd; - int mOutputFd; - std::vector<SimpleAtomMatcher> mPushedMatchers; - std::vector<PullInfo> mPulledInfo; - bool mClientAlive; - }; - - int claimToken(); - - bool readConfig(std::shared_ptr<SubscriptionInfo> subscriptionInfo); - - void spawnHelperThread(int myToken); - - void waitForSubscriptionToEndLocked(std::shared_ptr<SubscriptionInfo> myInfo, - int myToken, - std::unique_lock<std::mutex>& lock, - int timeoutSec); - - // Helper thread that pulls atoms at a regular frequency and sends - // heartbeats to perfd if statsd hasn't recently sent any data. Statsd must - // send heartbeats for perfd to escape a blocking read call and recheck if - // the user has terminated the subscription. - void pullAndSendHeartbeats(int myToken); - - void writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data, - const SimpleAtomMatcher& matcher); - - void getUidsForPullAtom(vector<int32_t>* uids, const PullInfo& pullInfo); - - void attemptWriteToPipeLocked(size_t dataSize); - - sp<UidMap> mUidMap; - - sp<StatsPullerManager> mPullerMgr; - - android::util::ProtoOutputStream mProto; - - mutable std::mutex mMutex; - - std::condition_variable mSubscriptionShouldEnd; - - std::shared_ptr<SubscriptionInfo> mSubscriptionInfo = nullptr; - - int mToken = 0; - - const int32_t DEFAULT_PULL_UID = AID_SYSTEM; - - // Tracks when we last send data to perfd. We need that time to determine - // when next to send a heartbeat. - int64_t mLastWriteMs = 0; - const int64_t kMsBetweenHeartbeats = 1000; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto deleted file mode 100644 index 07d0310ef2dd..000000000000 --- a/cmds/statsd/src/shell/shell_config.proto +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.os"; -option java_outer_classname = "ShellConfig"; - -import "frameworks/base/cmds/statsd/src/statsd_config.proto"; - -message PulledAtomSubscription { - optional SimpleAtomMatcher matcher = 1; - - /* gap between two pulls in milliseconds */ - optional int32 freq_millis = 2; - - /* Packages that the pull is requested from */ - repeated string packages = 3; -} - -message ShellSubscription { - repeated SimpleAtomMatcher pushed = 1; - repeated PulledAtomSubscription pulled = 2; -}
\ No newline at end of file diff --git a/cmds/statsd/src/shell/shell_data.proto b/cmds/statsd/src/shell/shell_data.proto deleted file mode 100644 index ec41cbc5caff..000000000000 --- a/cmds/statsd/src/shell/shell_data.proto +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.os.statsd"; -option java_outer_classname = "ShellDataProto"; - -import "frameworks/proto_logging/stats/atoms.proto"; - -// The output of shell subscription, including both pulled and pushed subscriptions. -message ShellData { - repeated Atom atom = 1; -} diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp deleted file mode 100755 index b877cc9c352f..000000000000 --- a/cmds/statsd/src/socket/StatsSocketListener.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include <ctype.h> -#include <limits.h> -#include <stdio.h> -#include <sys/cdefs.h> -#include <sys/prctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> -#include <unistd.h> - -#include <cutils/sockets.h> - -#include "StatsSocketListener.h" -#include "guardrail/StatsdStats.h" -#include "stats_log_util.h" - -namespace android { -namespace os { -namespace statsd { - -StatsSocketListener::StatsSocketListener(std::shared_ptr<LogEventQueue> queue) - : SocketListener(getLogSocket(), false /*start listen*/), mQueue(queue) { -} - -StatsSocketListener::~StatsSocketListener() { -} - -bool StatsSocketListener::onDataAvailable(SocketClient* cli) { - static bool name_set; - if (!name_set) { - prctl(PR_SET_NAME, "statsd.writer"); - name_set = true; - } - - // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received - char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1]; - struct iovec iov = {buffer, sizeof(buffer) - 1}; - - alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))]; - struct msghdr hdr = { - NULL, 0, &iov, 1, control, sizeof(control), 0, - }; - - int socket = cli->getSocket(); - - // To clear the entire buffer is secure/safe, but this contributes to 1.68% - // overhead under logging load. We are safe because we check counts, but - // still need to clear null terminator - // memset(buffer, 0, sizeof(buffer)); - ssize_t n = recvmsg(socket, &hdr, 0); - if (n <= (ssize_t)(sizeof(android_log_header_t))) { - return false; - } - - buffer[n] = 0; - - struct ucred* cred = NULL; - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); - while (cmsg != NULL) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { - cred = (struct ucred*)CMSG_DATA(cmsg); - break; - } - cmsg = CMSG_NXTHDR(&hdr, cmsg); - } - - struct ucred fake_cred; - if (cred == NULL) { - cred = &fake_cred; - cred->pid = 0; - cred->uid = DEFAULT_OVERFLOWUID; - } - - uint8_t* ptr = ((uint8_t*)buffer) + sizeof(android_log_header_t); - n -= sizeof(android_log_header_t); - - // When a log failed to write to statsd socket (e.g., due ot EBUSY), a special message would - // be sent to statsd when the socket communication becomes available again. - // The format is android_log_event_int_t with a single integer in the payload indicating the - // number of logs that failed. (*FORMAT MUST BE IN SYNC WITH system/core/libstats*) - // Note that all normal stats logs are in the format of event_list, so there won't be confusion. - // - // TODO(b/80538532): In addition to log it in StatsdStats, we should properly reset the config. - if (n == sizeof(android_log_event_long_t)) { - android_log_event_long_t* long_event = reinterpret_cast<android_log_event_long_t*>(ptr); - if (long_event->payload.type == EVENT_TYPE_LONG) { - int64_t composed_long = long_event->payload.data; - - // format: - // |last_tag|dropped_count| - int32_t dropped_count = (int32_t)(0xffffffff & composed_long); - int32_t last_atom_tag = (int32_t)((0xffffffff00000000 & (uint64_t)composed_long) >> 32); - - ALOGE("Found dropped events: %d error %d last atom tag %d from uid %d", dropped_count, - long_event->header.tag, last_atom_tag, cred->uid); - StatsdStats::getInstance().noteLogLost((int32_t)getWallClockSec(), dropped_count, - long_event->header.tag, last_atom_tag, cred->uid, - cred->pid); - return true; - } - } - - // move past the 4-byte StatsEventTag - uint8_t* msg = ptr + sizeof(uint32_t); - uint32_t len = n - sizeof(uint32_t); - uint32_t uid = cred->uid; - uint32_t pid = cred->pid; - - int64_t oldestTimestamp; - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(uid, pid); - logEvent->parseBuffer(msg, len); - - if (!mQueue->push(std::move(logEvent), &oldestTimestamp)) { - StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp); - } - - return true; -} - -int StatsSocketListener::getLogSocket() { - static const char socketName[] = "statsdw"; - int sock = android_get_control_socket(socketName); - - if (sock < 0) { // statsd started up in init.sh - sock = socket_local_server(socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM); - - int on = 1; - if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { - return -1; - } - } - return sock; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/socket/StatsSocketListener.h b/cmds/statsd/src/socket/StatsSocketListener.h deleted file mode 100644 index 2167a56445b9..000000000000 --- a/cmds/statsd/src/socket/StatsSocketListener.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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. - */ -#pragma once - -#include <sysutils/SocketListener.h> -#include <utils/RefBase.h> -#include "logd/LogEventQueue.h" - -// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of -// the uapi headers for userspace to use. This value is filled in on the -// out-of-band socket credentials if the OS fails to find one available. -// One of the causes of this is if SO_PASSCRED is set, all the packets before -// that point will have this value. We also use it in a fake credential if -// no socket credentials are supplied. -#ifndef DEFAULT_OVERFLOWUID -#define DEFAULT_OVERFLOWUID 65534 -#endif - -namespace android { -namespace os { -namespace statsd { - -class StatsSocketListener : public SocketListener, public virtual android::RefBase { -public: - explicit StatsSocketListener(std::shared_ptr<LogEventQueue> queue); - - virtual ~StatsSocketListener(); - -protected: - virtual bool onDataAvailable(SocketClient* cli); - -private: - static int getLogSocket(); - /** - * Who is going to get the events when they're read. - */ - std::shared_ptr<LogEventQueue> mQueue; -}; -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h deleted file mode 100644 index 63880017ca18..000000000000 --- a/cmds/statsd/src/state/StateListener.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include <utils/RefBase.h> - -#include "HashableDimensionKey.h" - -namespace android { -namespace os { -namespace statsd { - -class StateListener : public virtual RefBase { -public: - StateListener(){}; - - virtual ~StateListener(){}; - - /** - * Interface for handling a state change. - * - * The old and new state values map to the original state values. - * StateTrackers only track the original state values and are unaware - * of higher-level state groups. MetricProducers hold information on - * state groups and are responsible for mapping original state values to - * the correct state group. - * - * [eventTimeNs]: Time of the state change log event. - * [atomId]: The id of the state atom - * [primaryKey]: The primary field values of the state atom - * [oldState]: Previous state value before state change - * [newState]: Current state value after state change - */ - virtual void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const FieldValue& oldState, - const FieldValue& newState) = 0; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp deleted file mode 100644 index c29afeb794fa..000000000000 --- a/cmds/statsd/src/state/StateManager.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StateManager.h" - -#include <private/android_filesystem_config.h> - -namespace android { -namespace os { -namespace statsd { - -StateManager::StateManager() - : mAllowedPkg({ - "com.android.systemui", - }) { -} - -StateManager& StateManager::getInstance() { - static StateManager sStateManager; - return sStateManager; -} - -void StateManager::clear() { - mStateTrackers.clear(); -} - -void StateManager::onLogEvent(const LogEvent& event) { - // Only process state events from uids in AID_* and packages that are whitelisted in - // mAllowedPkg. - // Whitelisted AIDs are AID_ROOT and all AIDs in [1000, 2000) - if (event.GetUid() == AID_ROOT || (event.GetUid() >= 1000 && event.GetUid() < 2000) || - mAllowedLogSources.find(event.GetUid()) != mAllowedLogSources.end()) { - if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) { - mStateTrackers[event.GetTagId()]->onLogEvent(event); - } - } -} - -void StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) { - // Check if state tracker already exists. - if (mStateTrackers.find(atomId) == mStateTrackers.end()) { - mStateTrackers[atomId] = new StateTracker(atomId); - } - mStateTrackers[atomId]->registerListener(listener); -} - -void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) { - std::unique_lock<std::mutex> lock(mMutex); - - // Hold the sp<> until the lock is released so that ~StateTracker() is - // not called while the lock is held. - sp<StateTracker> toRemove; - - // Unregister listener from correct StateTracker - auto it = mStateTrackers.find(atomId); - if (it != mStateTrackers.end()) { - it->second->unregisterListener(listener); - - // Remove the StateTracker if it has no listeners - if (it->second->getListenersCount() == 0) { - toRemove = it->second; - mStateTrackers.erase(it); - } - } else { - ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist", - atomId); - } - lock.unlock(); -} - -bool StateManager::getStateValue(const int32_t atomId, const HashableDimensionKey& key, - FieldValue* output) const { - auto it = mStateTrackers.find(atomId); - if (it != mStateTrackers.end()) { - return it->second->getStateValue(key, output); - } - return false; -} - -void StateManager::updateLogSources(const sp<UidMap>& uidMap) { - mAllowedLogSources.clear(); - for (const auto& pkg : mAllowedPkg) { - auto uids = uidMap->getAppUid(pkg); - mAllowedLogSources.insert(uids.begin(), uids.end()); - } -} - -void StateManager::notifyAppChanged(const string& apk, const sp<UidMap>& uidMap) { - if (mAllowedPkg.find(apk) != mAllowedPkg.end()) { - updateLogSources(uidMap); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h deleted file mode 100644 index 18c404c29c4e..000000000000 --- a/cmds/statsd/src/state/StateManager.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include <inttypes.h> -#include <utils/RefBase.h> - -#include <set> -#include <string> -#include <unordered_map> - -#include "HashableDimensionKey.h" -#include "packages/UidMap.h" -#include "state/StateListener.h" -#include "state/StateTracker.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * This class is NOT thread safe. - * It should only be used while StatsLogProcessor's lock is held. - */ -class StateManager : public virtual RefBase { -public: - StateManager(); - - ~StateManager(){}; - - // Returns a pointer to the single, shared StateManager object. - static StateManager& getInstance(); - - // Unregisters all listeners and removes all trackers from StateManager. - void clear(); - - // Notifies the correct StateTracker of an event. - void onLogEvent(const LogEvent& event); - - // Notifies the StateTracker for the given atomId to register listener. - // If the correct StateTracker does not exist, a new StateTracker is created. - // Note: StateTrackers can be created for non-state atoms. They are essentially empty and - // do not perform any actions. - void registerListener(const int32_t atomId, wp<StateListener> listener); - - // Notifies the correct StateTracker to unregister a listener - // and removes the tracker if it no longer has any listeners. - void unregisterListener(const int32_t atomId, wp<StateListener> listener); - - // Returns true if the StateTracker exists and queries for the - // original state value mapped to the given query key. The state value is - // stored and output in a FieldValue class. - // Returns false if the StateTracker doesn't exist. - bool getStateValue(const int32_t atomId, const HashableDimensionKey& queryKey, - FieldValue* output) const; - - // Updates mAllowedLogSources with the latest uids for the packages that are allowed to log. - void updateLogSources(const sp<UidMap>& uidMap); - - void notifyAppChanged(const string& apk, const sp<UidMap>& uidMap); - - inline int getStateTrackersCount() const { - return mStateTrackers.size(); - } - - inline int getListenersCount(const int32_t atomId) const { - auto it = mStateTrackers.find(atomId); - if (it != mStateTrackers.end()) { - return it->second->getListenersCount(); - } - return -1; - } - -private: - mutable std::mutex mMutex; - - // Maps state atom ids to StateTrackers - std::unordered_map<int32_t, sp<StateTracker>> mStateTrackers; - - // The package names that can log state events. - const std::set<std::string> mAllowedPkg; - - // The combined uid sources (after translating pkg name to uid). - // State events from uids that are not in the list will be ignored to avoid state pollution. - std::set<int32_t> mAllowedLogSources; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp deleted file mode 100644 index 41e525c343ba..000000000000 --- a/cmds/statsd/src/state/StateTracker.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG true // STOPSHIP if true -#include "Log.h" - -#include "stats_util.h" - -#include "StateTracker.h" - -namespace android { -namespace os { -namespace statsd { - -StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) { -} - -void StateTracker::onLogEvent(const LogEvent& event) { - const int64_t eventTimeNs = event.GetElapsedTimestampNs(); - - // Parse event for primary field values i.e. primary key. - HashableDimensionKey primaryKey; - filterPrimaryKey(event.getValues(), &primaryKey); - - FieldValue newState; - if (!getStateFieldValueFromLogEvent(event, &newState)) { - ALOGE("StateTracker error extracting state from log event. Missing exclusive state field."); - clearStateForPrimaryKey(eventTimeNs, primaryKey); - return; - } - - mField.setField(newState.mField.getField()); - - if (newState.mValue.getType() != INT) { - ALOGE("StateTracker error extracting state from log event. Type: %d", - newState.mValue.getType()); - clearStateForPrimaryKey(eventTimeNs, primaryKey); - return; - } - - if (int resetState = event.getResetState(); resetState != -1) { - VLOG("StateTracker new reset state: %d", resetState); - const FieldValue resetStateFieldValue(mField, Value(resetState)); - handleReset(eventTimeNs, resetStateFieldValue); - return; - } - - const bool nested = newState.mAnnotations.isNested(); - StateValueInfo* stateValueInfo = &mStateMap[primaryKey]; - updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo); -} - -void StateTracker::registerListener(wp<StateListener> listener) { - mListeners.insert(listener); -} - -void StateTracker::unregisterListener(wp<StateListener> listener) { - mListeners.erase(listener); -} - -bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const { - output->mField = mField; - - if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) { - output->mValue = it->second.state; - return true; - } - - // Set the state value to kStateUnknown if query key is not found in state map. - output->mValue = kStateUnknown; - return false; -} - -void StateTracker::handleReset(const int64_t eventTimeNs, const FieldValue& newState) { - VLOG("StateTracker handle reset"); - for (auto& [primaryKey, stateValueInfo] : mStateMap) { - updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, - false /* nested; treat this state change as not nested */, - &stateValueInfo); - } -} - -void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs, - const HashableDimensionKey& primaryKey) { - VLOG("StateTracker clear state for primary key"); - const std::unordered_map<HashableDimensionKey, StateValueInfo>::iterator it = - mStateMap.find(primaryKey); - - // If there is no entry for the primaryKey in mStateMap, then the state is already - // kStateUnknown. - const FieldValue state(mField, Value(kStateUnknown)); - if (it != mStateMap.end()) { - updateStateForPrimaryKey(eventTimeNs, primaryKey, state, - false /* nested; treat this state change as not nested */, - &it->second); - } -} - -void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs, - const HashableDimensionKey& primaryKey, - const FieldValue& newState, const bool nested, - StateValueInfo* stateValueInfo) { - FieldValue oldState; - oldState.mField = mField; - oldState.mValue.setInt(stateValueInfo->state); - const int32_t oldStateValue = stateValueInfo->state; - const int32_t newStateValue = newState.mValue.int_value; - - if (kStateUnknown == newStateValue) { - mStateMap.erase(primaryKey); - } - - // Update state map for non-nested counting case. - // Every state event triggers a state overwrite. - if (!nested) { - stateValueInfo->state = newStateValue; - stateValueInfo->count = 1; - - // Notify listeners if state has changed. - if (oldStateValue != newStateValue) { - notifyListeners(eventTimeNs, primaryKey, oldState, newState); - } - return; - } - - // Update state map for nested counting case. - // - // Nested counting is only allowed for binary state events such as ON/OFF or - // ACQUIRE/RELEASE. For example, WakelockStateChanged might have the state - // events: ON, ON, OFF. The state will still be ON until we see the same - // number of OFF events as ON events. - // - // In atoms.proto, a state atom with nested counting enabled - // must only have 2 states. There is no enforcemnt here of this requirement. - // The atom must be logged correctly. - if (kStateUnknown == newStateValue) { - if (kStateUnknown != oldStateValue) { - notifyListeners(eventTimeNs, primaryKey, oldState, newState); - } - } else if (oldStateValue == kStateUnknown) { - stateValueInfo->state = newStateValue; - stateValueInfo->count = 1; - notifyListeners(eventTimeNs, primaryKey, oldState, newState); - } else if (oldStateValue == newStateValue) { - stateValueInfo->count++; - } else if (--stateValueInfo->count == 0) { - stateValueInfo->state = newStateValue; - stateValueInfo->count = 1; - notifyListeners(eventTimeNs, primaryKey, oldState, newState); - } -} - -void StateTracker::notifyListeners(const int64_t eventTimeNs, - const HashableDimensionKey& primaryKey, - const FieldValue& oldState, const FieldValue& newState) { - for (auto l : mListeners) { - auto sl = l.promote(); - if (sl != nullptr) { - sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState); - } - } -} - -bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output) { - const int exclusiveStateFieldIndex = event.getExclusiveStateFieldIndex(); - if (-1 == exclusiveStateFieldIndex) { - ALOGE("error extracting state from log event. Missing exclusive state field."); - return false; - } - - *output = event.getValues()[exclusiveStateFieldIndex]; - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h deleted file mode 100644 index abd579e7e302..000000000000 --- a/cmds/statsd/src/state/StateTracker.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include <utils/RefBase.h> -#include "HashableDimensionKey.h" -#include "logd/LogEvent.h" - -#include "state/StateListener.h" - -#include <unordered_map> - -namespace android { -namespace os { -namespace statsd { - -class StateTracker : public virtual RefBase { -public: - StateTracker(const int32_t atomId); - - virtual ~StateTracker(){}; - - // Updates state map and notifies all listeners if a state change occurs. - // Checks if a state change has occurred by getting the state value from - // the log event and comparing the old and new states. - void onLogEvent(const LogEvent& event); - - // Adds new listeners to set of StateListeners. If a listener is already - // registered, it is ignored. - void registerListener(wp<StateListener> listener); - - void unregisterListener(wp<StateListener> listener); - - // The output is a FieldValue object that has mStateField as the field and - // the original state value (found using the given query key) as the value. - // - // If the key isn't mapped to a state or the key size doesn't match the - // number of primary fields, the output value is set to kStateUnknown. - bool getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const; - - inline int getListenersCount() const { - return mListeners.size(); - } - - const static int kStateUnknown = -1; - -private: - struct StateValueInfo { - int32_t state = kStateUnknown; // state value - int count = 0; // nested count (only used for binary states) - }; - - Field mField; - - // Maps primary key to state value info - std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap; - - // Set of all StateListeners (objects listening for state changes) - std::set<wp<StateListener>> mListeners; - - // Reset all state values in map to the given state. - void handleReset(const int64_t eventTimeNs, const FieldValue& newState); - - // Clears the state value mapped to the given primary key by setting it to kStateUnknown. - void clearStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey); - - // Update the StateMap based on the received state value. - void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey, - const FieldValue& newState, const bool nested, - StateValueInfo* stateValueInfo); - - // Notify registered state listeners of state change. - void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey, - const FieldValue& oldState, const FieldValue& newState); -}; - -bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto deleted file mode 100644 index bb0796326f01..000000000000 --- a/cmds/statsd/src/stats_log.proto +++ /dev/null @@ -1,553 +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. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.os"; -option java_outer_classname = "StatsLog"; - -import "frameworks/proto_logging/stats/atoms.proto"; - -message DimensionsValue { - optional int32 field = 1; - - oneof value { - string value_str = 2; - int32 value_int = 3; - int64 value_long = 4; - bool value_bool = 5; - float value_float = 6; - DimensionsValueTuple value_tuple = 7; - uint64 value_str_hash = 8; - } -} - -message DimensionsValueTuple { - repeated DimensionsValue dimensions_value = 1; -} - -message StateValue { - optional int32 atom_id = 1; - - oneof contents { - int64 group_id = 2; - int32 value = 3; - } -} - -message EventMetricData { - optional int64 elapsed_timestamp_nanos = 1; - - optional Atom atom = 2; - - optional int64 wall_clock_timestamp_nanos = 3 [deprecated = true]; -} - -message CountBucketInfo { - optional int64 start_bucket_elapsed_nanos = 1; - - optional int64 end_bucket_elapsed_nanos = 2; - - optional int64 count = 3; - - optional int64 bucket_num = 4; - - optional int64 start_bucket_elapsed_millis = 5; - - optional int64 end_bucket_elapsed_millis = 6; -} - -message CountMetricData { - optional DimensionsValue dimensions_in_what = 1; - - repeated StateValue slice_by_state = 6; - - repeated CountBucketInfo bucket_info = 3; - - repeated DimensionsValue dimension_leaf_values_in_what = 4; - - optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; - - repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; -} - -message DurationBucketInfo { - optional int64 start_bucket_elapsed_nanos = 1; - - optional int64 end_bucket_elapsed_nanos = 2; - - optional int64 duration_nanos = 3; - - optional int64 bucket_num = 4; - - optional int64 start_bucket_elapsed_millis = 5; - - optional int64 end_bucket_elapsed_millis = 6; -} - -message DurationMetricData { - optional DimensionsValue dimensions_in_what = 1; - - repeated StateValue slice_by_state = 6; - - repeated DurationBucketInfo bucket_info = 3; - - repeated DimensionsValue dimension_leaf_values_in_what = 4; - - optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; - - repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; -} - -message ValueBucketInfo { - optional int64 start_bucket_elapsed_nanos = 1; - - optional int64 end_bucket_elapsed_nanos = 2; - - optional int64 value = 3 [deprecated = true]; - - oneof single_value { - int64 value_long = 7 [deprecated = true]; - - double value_double = 8 [deprecated = true]; - } - - message Value { - optional int32 index = 1; - oneof value { - int64 value_long = 2; - double value_double = 3; - } - } - - repeated Value values = 9; - - optional int64 bucket_num = 4; - - optional int64 start_bucket_elapsed_millis = 5; - - optional int64 end_bucket_elapsed_millis = 6; - - optional int64 condition_true_nanos = 10; -} - -message ValueMetricData { - optional DimensionsValue dimensions_in_what = 1; - - repeated StateValue slice_by_state = 6; - - repeated ValueBucketInfo bucket_info = 3; - - repeated DimensionsValue dimension_leaf_values_in_what = 4; - - optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; - - repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; -} - -message GaugeBucketInfo { - optional int64 start_bucket_elapsed_nanos = 1; - - optional int64 end_bucket_elapsed_nanos = 2; - - repeated Atom atom = 3; - - repeated int64 elapsed_timestamp_nanos = 4; - - repeated int64 wall_clock_timestamp_nanos = 5 [deprecated = true]; - - optional int64 bucket_num = 6; - - optional int64 start_bucket_elapsed_millis = 7; - - optional int64 end_bucket_elapsed_millis = 8; -} - -message GaugeMetricData { - optional DimensionsValue dimensions_in_what = 1; - - // Currently unsupported - repeated StateValue slice_by_state = 6; - - repeated GaugeBucketInfo bucket_info = 3; - - repeated DimensionsValue dimension_leaf_values_in_what = 4; - - optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; - - repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; -} - -message StatsLogReport { - optional int64 metric_id = 1; - - // Fields 2 and 3 are reserved. - - // Keep this in sync with BucketDropReason enum in MetricProducer.h. - enum BucketDropReason { - // For ValueMetric, a bucket is dropped during a dump report request iff - // current bucket should be included, a pull is needed (pulled metric and - // condition is true), and we are under fast time constraints. - DUMP_REPORT_REQUESTED = 1; - EVENT_IN_WRONG_BUCKET = 2; - CONDITION_UNKNOWN = 3; - PULL_FAILED = 4; - PULL_DELAYED = 5; - DIMENSION_GUARDRAIL_REACHED = 6; - MULTIPLE_BUCKETS_SKIPPED = 7; - // Not an invalid bucket case, but the bucket is dropped. - BUCKET_TOO_SMALL = 8; - // Not an invalid bucket case, but the bucket is skipped. - NO_DATA = 9; - }; - - message DropEvent { - optional BucketDropReason drop_reason = 1; - - optional int64 drop_time_millis = 2; - } - - message SkippedBuckets { - optional int64 start_bucket_elapsed_nanos = 1; - - optional int64 end_bucket_elapsed_nanos = 2; - - optional int64 start_bucket_elapsed_millis = 3; - - optional int64 end_bucket_elapsed_millis = 4; - - // The number of drop events is capped by StatsdStats::kMaxLoggedBucketDropEvents. - // The current maximum is 10 drop events. - repeated DropEvent drop_event = 5; - } - - message EventMetricDataWrapper { - repeated EventMetricData data = 1; - } - message CountMetricDataWrapper { - repeated CountMetricData data = 1; - } - message DurationMetricDataWrapper { - repeated DurationMetricData data = 1; - } - message ValueMetricDataWrapper { - repeated ValueMetricData data = 1; - repeated SkippedBuckets skipped = 2; - } - - message GaugeMetricDataWrapper { - repeated GaugeMetricData data = 1; - repeated SkippedBuckets skipped = 2; - } - - oneof data { - EventMetricDataWrapper event_metrics = 4; - CountMetricDataWrapper count_metrics = 5; - DurationMetricDataWrapper duration_metrics = 6; - ValueMetricDataWrapper value_metrics = 7; - GaugeMetricDataWrapper gauge_metrics = 8; - } - - optional int64 time_base_elapsed_nano_seconds = 9; - - optional int64 bucket_size_nano_seconds = 10; - - optional DimensionsValue dimensions_path_in_what = 11; - - optional DimensionsValue dimensions_path_in_condition = 12 [deprecated = true]; - - // DO NOT USE field 13. - - optional bool is_active = 14; -} - -message UidMapping { - message PackageInfoSnapshot { - message PackageInfo { - optional string name = 1; - - optional int64 version = 2; - - optional int32 uid = 3; - - optional bool deleted = 4; - - optional uint64 name_hash = 5; - - optional string version_string = 6; - - optional uint64 version_string_hash = 7; - - optional string installer = 8; - - optional uint64 installer_hash = 9; - } - optional int64 elapsed_timestamp_nanos = 1; - - repeated PackageInfo package_info = 2; - } - repeated PackageInfoSnapshot snapshots = 1; - - message Change { - optional bool deletion = 1; - - optional int64 elapsed_timestamp_nanos = 2; - optional string app = 3; - optional int32 uid = 4; - - optional int64 new_version = 5; - optional int64 prev_version = 6; - optional uint64 app_hash = 7; - optional string new_version_string = 8; - optional string prev_version_string = 9; - optional uint64 new_version_string_hash = 10; - optional uint64 prev_version_string_hash = 11; - } - repeated Change changes = 2; -} - -message ConfigMetricsReport { - repeated StatsLogReport metrics = 1; - - optional UidMapping uid_map = 2; - - optional int64 last_report_elapsed_nanos = 3; - - optional int64 current_report_elapsed_nanos = 4; - - optional int64 last_report_wall_clock_nanos = 5; - - optional int64 current_report_wall_clock_nanos = 6; - - message Annotation { - optional int64 field_int64 = 1; - optional int32 field_int32 = 2; - } - repeated Annotation annotation = 7; - - enum DumpReportReason { - DEVICE_SHUTDOWN = 1; - CONFIG_UPDATED = 2; - CONFIG_REMOVED = 3; - GET_DATA_CALLED = 4; - ADB_DUMP = 5; - CONFIG_RESET = 6; - STATSCOMPANION_DIED = 7; - TERMINATION_SIGNAL_RECEIVED = 8; - } - optional DumpReportReason dump_report_reason = 8; - - repeated string strings = 9; -} - -message ConfigMetricsReportList { - message ConfigKey { - optional int32 uid = 1; - optional int64 id = 2; - } - optional ConfigKey config_key = 1; - - repeated ConfigMetricsReport reports = 2; - - reserved 10; -} - -message StatsdStatsReport { - optional int32 stats_begin_time_sec = 1; - - optional int32 stats_end_time_sec = 2; - - message MatcherStats { - optional int64 id = 1; - optional int32 matched_times = 2; - } - - message ConditionStats { - optional int64 id = 1; - optional int32 max_tuple_counts = 2; - } - - message MetricStats { - optional int64 id = 1; - optional int32 max_tuple_counts = 2; - } - - message AlertStats { - optional int64 id = 1; - optional int32 alerted_times = 2; - } - - message ConfigStats { - optional int32 uid = 1; - optional int64 id = 2; - optional int32 creation_time_sec = 3; - optional int32 deletion_time_sec = 4; - optional int32 reset_time_sec = 19; - optional int32 metric_count = 5; - optional int32 condition_count = 6; - optional int32 matcher_count = 7; - optional int32 alert_count = 8; - optional bool is_valid = 9; - repeated int32 broadcast_sent_time_sec = 10; - repeated int32 data_drop_time_sec = 11; - repeated int64 data_drop_bytes = 21; - repeated int32 dump_report_time_sec = 12; - repeated int32 dump_report_data_size = 20; - repeated MatcherStats matcher_stats = 13; - repeated ConditionStats condition_stats = 14; - repeated MetricStats metric_stats = 15; - repeated AlertStats alert_stats = 16; - repeated MetricStats metric_dimension_in_condition_stats = 17 [deprecated = true]; - message Annotation { - optional int64 field_int64 = 1; - optional int32 field_int32 = 2; - } - repeated Annotation annotation = 18; - repeated int32 activation_time_sec = 22; - repeated int32 deactivation_time_sec = 23; - } - - repeated ConfigStats config_stats = 3; - - message AtomStats { - optional int32 tag = 1; - optional int32 count = 2; - optional int32 error_count = 3; - } - - repeated AtomStats atom_stats = 7; - - message UidMapStats { - optional int32 changes = 1; - optional int32 bytes_used = 2; - optional int32 dropped_changes = 3; - optional int32 deleted_apps = 4; - } - optional UidMapStats uidmap_stats = 8; - - message AnomalyAlarmStats { - optional int32 alarms_registered = 1; - } - optional AnomalyAlarmStats anomaly_alarm_stats = 9; - - message PulledAtomStats { - optional int32 atom_id = 1; - optional int64 total_pull = 2; - optional int64 total_pull_from_cache = 3; - optional int64 min_pull_interval_sec = 4; - optional int64 average_pull_time_nanos = 5; - optional int64 max_pull_time_nanos = 6; - optional int64 average_pull_delay_nanos = 7; - optional int64 max_pull_delay_nanos = 8; - optional int64 data_error = 9; - optional int64 pull_timeout = 10; - optional int64 pull_exceed_max_delay = 11; - optional int64 pull_failed = 12; - optional int64 stats_companion_pull_failed = 13 [deprecated = true]; - optional int64 stats_companion_pull_binder_transaction_failed = 14 [deprecated = true]; - optional int64 empty_data = 15; - optional int64 registered_count = 16; - optional int64 unregistered_count = 17; - optional int32 atom_error_count = 18; - optional int64 binder_call_failed = 19; - optional int64 failed_uid_provider_not_found = 20; - optional int64 puller_not_found = 21; - message PullTimeoutMetadata { - optional int64 pull_timeout_uptime_millis = 1; - optional int64 pull_timeout_elapsed_millis = 2; - } - repeated PullTimeoutMetadata pull_atom_metadata = 22; - } - repeated PulledAtomStats pulled_atom_stats = 10; - - message AtomMetricStats { - optional int64 metric_id = 1; - optional int64 hard_dimension_limit_reached = 2; - optional int64 late_log_event_skipped = 3; - optional int64 skipped_forward_buckets = 4; - optional int64 bad_value_type = 5; - optional int64 condition_change_in_next_bucket = 6; - optional int64 invalidated_bucket = 7; - optional int64 bucket_dropped = 8; - optional int64 min_bucket_boundary_delay_ns = 9; - optional int64 max_bucket_boundary_delay_ns = 10; - optional int64 bucket_unknown_condition = 11; - optional int64 bucket_count = 12; - } - repeated AtomMetricStats atom_metric_stats = 17; - - message LoggerErrorStats { - optional int32 logger_disconnection_sec = 1; - optional int32 error_code = 2; - } - repeated LoggerErrorStats logger_error_stats = 11; - - message PeriodicAlarmStats { - optional int32 alarms_registered = 1; - } - optional PeriodicAlarmStats periodic_alarm_stats = 12; - - message SkippedLogEventStats { - optional int32 tag = 1; - optional int64 elapsed_timestamp_nanos = 2; - } - repeated SkippedLogEventStats skipped_log_event_stats = 13; - - repeated int64 log_loss_stats = 14; - - repeated int32 system_restart_sec = 15; - - message LogLossStats { - optional int32 detected_time_sec = 1; - optional int32 count = 2; - optional int32 last_error = 3; - optional int32 last_tag = 4; - optional int32 uid = 5; - optional int32 pid = 6; - } - repeated LogLossStats detected_log_loss = 16; - - message EventQueueOverflow { - optional int32 count = 1; - optional int64 max_queue_history_ns = 2; - optional int64 min_queue_history_ns = 3; - } - - optional EventQueueOverflow queue_overflow = 18; - - message ActivationBroadcastGuardrail { - optional int32 uid = 1; - repeated int32 guardrail_met_sec = 2; - } - - repeated ActivationBroadcastGuardrail activation_guardrail_stats = 19; -} - -message AlertTriggerDetails { - message MetricValue { - optional int64 metric_id = 1; - optional DimensionsValue dimension_in_what = 2; - optional DimensionsValue dimension_in_condition = 3 [deprecated = true]; - optional int64 value = 4; - } - oneof value { - MetricValue trigger_metric = 1; - EventMetricData trigger_event = 2; - } - optional UidMapping.PackageInfoSnapshot package_info = 3; -} diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp deleted file mode 100644 index 423bae8bc0a4..000000000000 --- a/cmds/statsd/src/stats_log_util.cpp +++ /dev/null @@ -1,609 +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. - */ - -#include "hash.h" -#include "stats_log_util.h" - -#include <aidl/android/os/IStatsCompanionService.h> -#include <private/android_filesystem_config.h> -#include <set> -#include <utils/SystemClock.h> - -#include "statscompanion_util.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FIXED64; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::FIELD_TYPE_UINT64; -using android::util::ProtoOutputStream; - -using aidl::android::os::IStatsCompanionService; -using std::shared_ptr; -using std::string; - -namespace android { -namespace os { -namespace statsd { - -// for DimensionsValue Proto -const int DIMENSIONS_VALUE_FIELD = 1; -const int DIMENSIONS_VALUE_VALUE_STR = 2; -const int DIMENSIONS_VALUE_VALUE_INT = 3; -const int DIMENSIONS_VALUE_VALUE_LONG = 4; -// const int DIMENSIONS_VALUE_VALUE_BOOL = 5; // logd doesn't have bool data type. -const int DIMENSIONS_VALUE_VALUE_FLOAT = 6; -const int DIMENSIONS_VALUE_VALUE_TUPLE = 7; -const int DIMENSIONS_VALUE_VALUE_STR_HASH = 8; - -const int DIMENSIONS_VALUE_TUPLE_VALUE = 1; - -// for StateValue Proto -const int STATE_VALUE_ATOM_ID = 1; -const int STATE_VALUE_CONTENTS_GROUP_ID = 2; -const int STATE_VALUE_CONTENTS_VALUE = 3; - -// for PulledAtomStats proto -const int FIELD_ID_PULLED_ATOM_STATS = 10; -const int FIELD_ID_PULL_ATOM_ID = 1; -const int FIELD_ID_TOTAL_PULL = 2; -const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3; -const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4; -const int FIELD_ID_AVERAGE_PULL_TIME_NANOS = 5; -const int FIELD_ID_MAX_PULL_TIME_NANOS = 6; -const int FIELD_ID_AVERAGE_PULL_DELAY_NANOS = 7; -const int FIELD_ID_MAX_PULL_DELAY_NANOS = 8; -const int FIELD_ID_DATA_ERROR = 9; -const int FIELD_ID_PULL_TIMEOUT = 10; -const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11; -const int FIELD_ID_PULL_FAILED = 12; -const int FIELD_ID_EMPTY_DATA = 15; -const int FIELD_ID_PULL_REGISTERED_COUNT = 16; -const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17; -const int FIELD_ID_ATOM_ERROR_COUNT = 18; -const int FIELD_ID_BINDER_CALL_FAIL_COUNT = 19; -const int FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND = 20; -const int FIELD_ID_PULLER_NOT_FOUND = 21; -const int FIELD_ID_PULL_TIMEOUT_METADATA = 22; -const int FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS = 1; -const int FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS = 2; - -// for AtomMetricStats proto -const int FIELD_ID_ATOM_METRIC_STATS = 17; -const int FIELD_ID_METRIC_ID = 1; -const int FIELD_ID_HARD_DIMENSION_LIMIT_REACHED = 2; -const int FIELD_ID_LATE_LOG_EVENT_SKIPPED = 3; -const int FIELD_ID_SKIPPED_FORWARD_BUCKETS = 4; -const int FIELD_ID_BAD_VALUE_TYPE = 5; -const int FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET = 6; -const int FIELD_ID_INVALIDATED_BUCKET = 7; -const int FIELD_ID_BUCKET_DROPPED = 8; -const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9; -const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10; -const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11; -const int FIELD_ID_BUCKET_COUNT = 12; - -namespace { - -void writeDimensionToProtoHelper(const std::vector<FieldValue>& dims, size_t* index, int depth, - int prefix, std::set<string> *str_set, - ProtoOutputStream* protoOutput) { - size_t count = dims.size(); - while (*index < count) { - const auto& dim = dims[*index]; - const int valueDepth = dim.mField.getDepth(); - const int valuePrefix = dim.mField.getPrefix(depth); - const int fieldNum = dim.mField.getPosAtDepth(depth); - if (valueDepth > 2) { - ALOGE("Depth > 2 not supported"); - return; - } - - if (depth == valueDepth && valuePrefix == prefix) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - DIMENSIONS_VALUE_TUPLE_VALUE); - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); - switch (dim.mValue.getType()) { - case INT: - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT, - dim.mValue.int_value); - break; - case LONG: - protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG, - (long long)dim.mValue.long_value); - break; - case FLOAT: - protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT, - dim.mValue.float_value); - break; - case STRING: - if (str_set == nullptr) { - protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR, - dim.mValue.str_value); - } else { - str_set->insert(dim.mValue.str_value); - protoOutput->write( - FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH, - (long long)Hash64(dim.mValue.str_value)); - } - break; - default: - break; - } - if (token != 0) { - protoOutput->end(token); - } - (*index)++; - } else if (valueDepth > depth && valuePrefix == prefix) { - // Writing the sub tree - uint64_t dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE); - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); - uint64_t tupleToken = - protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); - writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth), - str_set, protoOutput); - protoOutput->end(tupleToken); - protoOutput->end(dimensionToken); - } else { - // Done with the prev sub tree - return; - } - } -} - -void writeDimensionLeafToProtoHelper(const std::vector<FieldValue>& dims, - const int dimensionLeafField, - size_t* index, int depth, - int prefix, std::set<string> *str_set, - ProtoOutputStream* protoOutput) { - size_t count = dims.size(); - while (*index < count) { - const auto& dim = dims[*index]; - const int valueDepth = dim.mField.getDepth(); - const int valuePrefix = dim.mField.getPrefix(depth); - if (valueDepth > 2) { - ALOGE("Depth > 2 not supported"); - return; - } - - if (depth == valueDepth && valuePrefix == prefix) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - dimensionLeafField); - switch (dim.mValue.getType()) { - case INT: - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT, - dim.mValue.int_value); - break; - case LONG: - protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG, - (long long)dim.mValue.long_value); - break; - case FLOAT: - protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT, - dim.mValue.float_value); - break; - case STRING: - if (str_set == nullptr) { - protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR, - dim.mValue.str_value); - } else { - str_set->insert(dim.mValue.str_value); - protoOutput->write( - FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH, - (long long)Hash64(dim.mValue.str_value)); - } - break; - default: - break; - } - if (token != 0) { - protoOutput->end(token); - } - (*index)++; - } else if (valueDepth > depth && valuePrefix == prefix) { - writeDimensionLeafToProtoHelper(dims, dimensionLeafField, - index, valueDepth, dim.mField.getPrefix(valueDepth), - str_set, protoOutput); - } else { - // Done with the prev sub tree - return; - } - } -} - -void writeDimensionPathToProtoHelper(const std::vector<Matcher>& fieldMatchers, - size_t* index, int depth, int prefix, - ProtoOutputStream* protoOutput) { - size_t count = fieldMatchers.size(); - while (*index < count) { - const Field& field = fieldMatchers[*index].mMatcher; - const int valueDepth = field.getDepth(); - const int valuePrefix = field.getPrefix(depth); - const int fieldNum = field.getPosAtDepth(depth); - if (valueDepth > 2) { - ALOGE("Depth > 2 not supported"); - return; - } - - if (depth == valueDepth && valuePrefix == prefix) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - DIMENSIONS_VALUE_TUPLE_VALUE); - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); - if (token != 0) { - protoOutput->end(token); - } - (*index)++; - } else if (valueDepth > depth && valuePrefix == prefix) { - // Writing the sub tree - uint64_t dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE); - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); - uint64_t tupleToken = - protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); - writeDimensionPathToProtoHelper(fieldMatchers, index, valueDepth, - field.getPrefix(valueDepth), protoOutput); - protoOutput->end(tupleToken); - protoOutput->end(dimensionToken); - } else { - // Done with the prev sub tree - return; - } - } -} - -} // namespace - -void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set, - ProtoOutputStream* protoOutput) { - if (dimension.getValues().size() == 0) { - return; - } - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, - dimension.getValues()[0].mField.getTag()); - uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); - size_t index = 0; - writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, str_set, protoOutput); - protoOutput->end(topToken); -} - -void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension, - const int dimensionLeafFieldId, - std::set<string> *str_set, - ProtoOutputStream* protoOutput) { - if (dimension.getValues().size() == 0) { - return; - } - size_t index = 0; - writeDimensionLeafToProtoHelper(dimension.getValues(), dimensionLeafFieldId, - &index, 0, 0, str_set, protoOutput); -} - -void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers, - ProtoOutputStream* protoOutput) { - if (fieldMatchers.size() == 0) { - return; - } - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, - fieldMatchers[0].mMatcher.getTag()); - uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); - size_t index = 0; - writeDimensionPathToProtoHelper(fieldMatchers, &index, 0, 0, protoOutput); - protoOutput->end(topToken); -} - -// Supported Atoms format -// XYZ_Atom { -// repeated SubMsg field_1 = 1; -// SubMsg2 field_2 = 2; -// int32/float/string/int63 field_3 = 3; -// } -// logd's msg format, doesn't allow us to distinguish between the 2 cases below -// Case (1): -// Atom { -// SubMsg { -// int i = 1; -// int j = 2; -// } -// repeated SubMsg -// } -// -// and case (2): -// Atom { -// SubMsg { -// repeated int i = 1; -// repeated int j = 2; -// } -// optional SubMsg = 1; -// } -// -// -void writeFieldValueTreeToStreamHelper(int tagId, const std::vector<FieldValue>& dims, - size_t* index, int depth, int prefix, - ProtoOutputStream* protoOutput) { - size_t count = dims.size(); - while (*index < count) { - const auto& dim = dims[*index]; - const int valueDepth = dim.mField.getDepth(); - const int valuePrefix = dim.mField.getPrefix(depth); - const int fieldNum = dim.mField.getPosAtDepth(depth); - if (valueDepth > 2) { - ALOGE("Depth > 2 not supported"); - return; - } - - if (depth == valueDepth && valuePrefix == prefix) { - switch (dim.mValue.getType()) { - case INT: - protoOutput->write(FIELD_TYPE_INT32 | fieldNum, dim.mValue.int_value); - break; - case LONG: - protoOutput->write(FIELD_TYPE_INT64 | fieldNum, - (long long)dim.mValue.long_value); - break; - case FLOAT: - protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value); - break; - case STRING: { - protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value); - break; - } - case STORAGE: - protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum, - (const char*)dim.mValue.storage_value.data(), - dim.mValue.storage_value.size()); - break; - default: - break; - } - (*index)++; - } else if (valueDepth > depth && valuePrefix == prefix) { - // Writing the sub tree - uint64_t msg_token = 0ULL; - if (valueDepth == depth + 2) { - msg_token = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum); - } else if (valueDepth == depth + 1) { - msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum); - } - // Directly jump to the leaf value because the repeated position field is implied - // by the position of the sub msg in the parent field. - writeFieldValueTreeToStreamHelper(tagId, dims, index, valueDepth, - dim.mField.getPrefix(valueDepth), protoOutput); - if (msg_token != 0) { - protoOutput->end(msg_token); - } - } else { - // Done with the prev sub tree - return; - } - } -} - -void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values, - util::ProtoOutputStream* protoOutput) { - uint64_t atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId); - - size_t index = 0; - writeFieldValueTreeToStreamHelper(tagId, values, &index, 0, 0, protoOutput); - protoOutput->end(atomToken); -} - -void writeStateToProto(const FieldValue& state, util::ProtoOutputStream* protoOutput) { - protoOutput->write(FIELD_TYPE_INT32 | STATE_VALUE_ATOM_ID, state.mField.getTag()); - - switch (state.mValue.getType()) { - case INT: - protoOutput->write(FIELD_TYPE_INT32 | STATE_VALUE_CONTENTS_VALUE, - state.mValue.int_value); - break; - case LONG: - protoOutput->write(FIELD_TYPE_INT64 | STATE_VALUE_CONTENTS_GROUP_ID, - state.mValue.long_value); - break; - default: - break; - } -} - -int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) { - int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit); - if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL && - uid != AID_ROOT) { - bucketSizeMillis = 5 * 60 * 1000LL; - } - return bucketSizeMillis; -} - -int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) { - switch (unit) { - case ONE_MINUTE: - return 60 * 1000LL; - case FIVE_MINUTES: - return 5 * 60 * 1000LL; - case TEN_MINUTES: - return 10 * 60 * 1000LL; - case THIRTY_MINUTES: - return 30 * 60 * 1000LL; - case ONE_HOUR: - return 60 * 60 * 1000LL; - case THREE_HOURS: - return 3 * 60 * 60 * 1000LL; - case SIX_HOURS: - return 6 * 60 * 60 * 1000LL; - case TWELVE_HOURS: - return 12 * 60 * 60 * 1000LL; - case ONE_DAY: - return 24 * 60 * 60 * 1000LL; - case ONE_WEEK: - return 7 * 24 * 60 * 60 * 1000LL; - case CTS: - return 1000; - case TIME_UNIT_UNSPECIFIED: - default: - return -1; - } -} - -void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair, - util::ProtoOutputStream* protoOutput) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS | - FIELD_COUNT_REPEATED); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_PULL_ATOM_ID, (int32_t)pair.first); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL, (long long)pair.second.totalPull); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL_FROM_CACHE, - (long long)pair.second.totalPullFromCache); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_PULL_INTERVAL_SEC, - (long long)pair.second.minPullIntervalSec); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_AVERAGE_PULL_TIME_NANOS, - (long long)pair.second.avgPullTimeNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_TIME_NANOS, - (long long)pair.second.maxPullTimeNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_AVERAGE_PULL_DELAY_NANOS, - (long long)pair.second.avgPullDelayNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_DELAY_NANOS, - (long long)pair.second.maxPullDelayNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DATA_ERROR, (long long)pair.second.dataError); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT, - (long long)pair.second.pullTimeout); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_EXCEED_MAX_DELAY, - (long long)pair.second.pullExceedMaxDelay); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED, - (long long)pair.second.pullFailed); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA, - (long long)pair.second.emptyData); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT, - (long long) pair.second.registeredCount); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT, - (long long) pair.second.unregisteredCount); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BINDER_CALL_FAIL_COUNT, - (long long)pair.second.binderCallFailCount); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND, - (long long)pair.second.pullUidProviderNotFound); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULLER_NOT_FOUND, - (long long)pair.second.pullerNotFound); - for (const auto& pullTimeoutMetadata : pair.second.pullTimeoutMetadata) { - uint64_t timeoutMetadataToken = protoOutput->start(FIELD_TYPE_MESSAGE | - FIELD_ID_PULL_TIMEOUT_METADATA | - FIELD_COUNT_REPEATED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS, - pullTimeoutMetadata.pullTimeoutUptimeMillis); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS, - pullTimeoutMetadata.pullTimeoutElapsedMillis); - protoOutput->end(timeoutMetadataToken); - } - protoOutput->end(token); -} - -void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair, - util::ProtoOutputStream *protoOutput) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS | - FIELD_COUNT_REPEATED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED, - (long long)pair.second.hardDimensionLimitReached); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED, - (long long)pair.second.lateLogEventSkipped); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_FORWARD_BUCKETS, - (long long)pair.second.skippedForwardBuckets); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BAD_VALUE_TYPE, - (long long)pair.second.badValueType); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET, - (long long)pair.second.conditionChangeInNextBucket); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_INVALIDATED_BUCKET, - (long long)pair.second.invalidatedBucket); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_DROPPED, - (long long)pair.second.bucketDropped); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS, - (long long)pair.second.minBucketBoundaryDelayNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS, - (long long)pair.second.maxBucketBoundaryDelayNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION, - (long long)pair.second.bucketUnknownCondition); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_COUNT, - (long long)pair.second.bucketCount); - protoOutput->end(token); -} - -int64_t getElapsedRealtimeNs() { - return ::android::elapsedRealtimeNano(); -} - -int64_t getElapsedRealtimeSec() { - return ::android::elapsedRealtimeNano() / NS_PER_SEC; -} - -int64_t getElapsedRealtimeMillis() { - return ::android::elapsedRealtime(); -} - -int64_t getSystemUptimeMillis() { - return ::android::uptimeMillis(); -} - -int64_t getWallClockNs() { - return time(nullptr) * NS_PER_SEC; -} - -int64_t getWallClockSec() { - return time(nullptr); -} - -int64_t getWallClockMillis() { - return time(nullptr) * MS_PER_SEC; -} - -int64_t truncateTimestampIfNecessary(const LogEvent& event) { - if (event.shouldTruncateTimestamp() || - (event.GetTagId() >= StatsdStats::kTimestampTruncationStartTag && - event.GetTagId() <= StatsdStats::kTimestampTruncationEndTag)) { - return event.GetElapsedTimestampNs() / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60); - } else { - return event.GetElapsedTimestampNs(); - } -} - -int64_t NanoToMillis(const int64_t nano) { - return nano / 1000000; -} - -int64_t MillisToNano(const int64_t millis) { - return millis * 1000000; -} - -bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid) { - shared_ptr<IStatsCompanionService> scs = getStatsCompanionService(); - if (scs == nullptr) { - return false; - } - - bool success; - ::ndk::ScopedAStatus status = scs->checkPermission(string(permission), pid, uid, &success); - if (!status.isOk()) { - return false; - } - - return success; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h deleted file mode 100644 index 10e065e4113f..000000000000 --- a/cmds/statsd/src/stats_log_util.h +++ /dev/null @@ -1,117 +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. - */ - -#pragma once - -#include <android/util/ProtoOutputStream.h> - -#include "FieldValue.h" -#include "HashableDimensionKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" - -using android::util::ProtoOutputStream; - -namespace android { -namespace os { -namespace statsd { - -void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values, - ProtoOutputStream* protoOutput); -void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set, - ProtoOutputStream* protoOutput); - -void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension, - const int dimensionLeafFieldId, - std::set<string> *str_set, - ProtoOutputStream* protoOutput); - -void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers, - ProtoOutputStream* protoOutput); - -void writeStateToProto(const FieldValue& state, ProtoOutputStream* protoOutput); - -// Convert the TimeUnit enum to the bucket size in millis with a guardrail on -// bucket size. -int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit); - -// Convert the TimeUnit enum to the bucket size in millis. -int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit); - -// Gets the elapsed timestamp in ns. -int64_t getElapsedRealtimeNs(); - -// Gets the elapsed timestamp in millis. -int64_t getElapsedRealtimeMillis(); - -// Gets the elapsed timestamp in seconds. -int64_t getElapsedRealtimeSec(); - -// Gets the system uptime in millis. -int64_t getSystemUptimeMillis(); - -// Gets the wall clock timestamp in ns. -int64_t getWallClockNs(); - -// Gets the wall clock timestamp in millis. -int64_t getWallClockMillis(); - -// Gets the wall clock timestamp in seconds. -int64_t getWallClockSec(); - -int64_t NanoToMillis(const int64_t nano); - -int64_t MillisToNano(const int64_t millis); - -// Helper function to write PulledAtomStats to ProtoOutputStream -void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair, - ProtoOutputStream* protoOutput); - -// Helper function to write AtomMetricStats to ProtoOutputStream -void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair, - ProtoOutputStream *protoOutput); - -template<class T> -bool parseProtoOutputStream(ProtoOutputStream& protoOutput, T* message) { - std::string pbBytes; - sp<android::util::ProtoReader> reader = protoOutput.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - pbBytes.append(reinterpret_cast<const char*>(reader->readBuffer()), toRead); - reader->move(toRead); - } - return message->ParseFromArray(pbBytes.c_str(), pbBytes.size()); -} - -// Checks the truncate timestamp annotation as well as the restricted range of 300,000 - 304,999. -// Returns the truncated timestamp to the nearest 5 minutes if needed. -int64_t truncateTimestampIfNecessary(const LogEvent& event); - -// Checks permission for given pid and uid. -bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid); - -inline bool isVendorPulledAtom(int atomId) { - return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag; -} - -inline bool isPulledAtom(int atomId) { - return atomId >= StatsdStats::kPullAtomStartTag && atomId < StatsdStats::kVendorAtomStartTag; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h deleted file mode 100644 index cfc411fdd25f..000000000000 --- a/cmds/statsd/src/stats_util.h +++ /dev/null @@ -1,36 +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. - */ - -#pragma once - -#include "HashableDimensionKey.h" - -#include <unordered_map> - -namespace android { -namespace os { -namespace statsd { - -const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey(); -const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey(); - -typedef std::map<int64_t, HashableDimensionKey> ConditionKey; - -typedef std::unordered_map<MetricDimensionKey, int64_t> DimToValMap; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/statscompanion_util.cpp b/cmds/statsd/src/statscompanion_util.cpp deleted file mode 100644 index ce07ec0ea884..000000000000 --- a/cmds/statsd/src/statscompanion_util.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "statscompanion_util.h" -#include <android/binder_auto_utils.h> -#include <android/binder_manager.h> - -namespace android { -namespace os { -namespace statsd { - -shared_ptr<IStatsCompanionService> getStatsCompanionService() { - ::ndk::SpAIBinder binder(AServiceManager_getService("statscompanion")); - return IStatsCompanionService::fromBinder(binder); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/statscompanion_util.h b/cmds/statsd/src/statscompanion_util.h deleted file mode 100644 index e20c40bba104..000000000000 --- a/cmds/statsd/src/statscompanion_util.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include <aidl/android/os/IStatsCompanionService.h> - -using aidl::android::os::IStatsCompanionService; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -/** Fetches and returns the StatsCompanionService. */ -shared_ptr<IStatsCompanionService> getStatsCompanionService(); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto deleted file mode 100644 index acdffd3d4712..000000000000 --- a/cmds/statsd/src/statsd_config.proto +++ /dev/null @@ -1,511 +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. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.internal.os"; -option java_outer_classname = "StatsdConfigProto"; - -enum Position { - POSITION_UNKNOWN = 0; - - FIRST = 1; - - LAST = 2; - - ANY = 3; - - ALL = 4; -} - -enum TimeUnit { - TIME_UNIT_UNSPECIFIED = 0; - ONE_MINUTE = 1; // WILL BE GUARDRAILED TO 5 MINS UNLESS UID = SHELL OR ROOT - FIVE_MINUTES = 2; - TEN_MINUTES = 3; - THIRTY_MINUTES = 4; - ONE_HOUR = 5; - THREE_HOURS = 6; - SIX_HOURS = 7; - TWELVE_HOURS = 8; - ONE_DAY = 9; - ONE_WEEK = 10; - CTS = 1000; -} - -message FieldMatcher { - optional int32 field = 1; - - optional Position position = 2; - - repeated FieldMatcher child = 3; -} - -message FieldValueMatcher { - optional int32 field = 1; - - optional Position position = 2; - - oneof value_matcher { - bool eq_bool = 3; - string eq_string = 4; - int64 eq_int = 5; - - int64 lt_int = 6; - int64 gt_int = 7; - float lt_float = 8; - float gt_float = 9; - - int64 lte_int = 10; - int64 gte_int = 11; - - MessageMatcher matches_tuple = 12; - - StringListMatcher eq_any_string = 13; - StringListMatcher neq_any_string = 14; - } -} - -message MessageMatcher { - repeated FieldValueMatcher field_value_matcher = 1; -} - -message StringListMatcher { - repeated string str_value = 1; -} - -enum LogicalOperation { - LOGICAL_OPERATION_UNSPECIFIED = 0; - AND = 1; - OR = 2; - NOT = 3; - NAND = 4; - NOR = 5; -} - -message SimpleAtomMatcher { - optional int32 atom_id = 1; - - repeated FieldValueMatcher field_value_matcher = 2; -} - -message AtomMatcher { - optional int64 id = 1; - - message Combination { - optional LogicalOperation operation = 1; - - repeated int64 matcher = 2; - } - oneof contents { - SimpleAtomMatcher simple_atom_matcher = 2; - Combination combination = 3; - } -} - -message SimplePredicate { - optional int64 start = 1; - - optional int64 stop = 2; - - optional bool count_nesting = 3 [default = true]; - - optional int64 stop_all = 4; - - enum InitialValue { - UNKNOWN = 0; - FALSE = 1; - } - optional InitialValue initial_value = 5 [default = UNKNOWN]; - - optional FieldMatcher dimensions = 6; -} - -message Predicate { - optional int64 id = 1; - - message Combination { - optional LogicalOperation operation = 1; - - repeated int64 predicate = 2; - } - - oneof contents { - SimplePredicate simple_predicate = 2; - Combination combination = 3; - } -} - -message StateMap { - message StateGroup { - optional int64 group_id = 1; - - repeated int32 value = 2; - } - - repeated StateGroup group = 1; -} - -message State { - optional int64 id = 1; - - optional int32 atom_id = 2; - - optional StateMap map = 3; -} - -message MetricConditionLink { - optional int64 condition = 1; - - optional FieldMatcher fields_in_what = 2; - - optional FieldMatcher fields_in_condition = 3; -} - -message MetricStateLink { - optional int32 state_atom_id = 1; - - optional FieldMatcher fields_in_what = 2; - - optional FieldMatcher fields_in_state = 3; -} - -message FieldFilter { - optional bool include_all = 1 [default = false]; - optional FieldMatcher fields = 2; -} - -message EventMetric { - optional int64 id = 1; - - optional int64 what = 2; - - optional int64 condition = 3; - - repeated MetricConditionLink links = 4; - - reserved 100; - reserved 101; -} - -message CountMetric { - optional int64 id = 1; - - optional int64 what = 2; - - optional int64 condition = 3; - - optional FieldMatcher dimensions_in_what = 4; - - repeated int64 slice_by_state = 8; - - optional TimeUnit bucket = 5; - - repeated MetricConditionLink links = 6; - - repeated MetricStateLink state_link = 9; - - optional FieldMatcher dimensions_in_condition = 7 [deprecated = true]; - - reserved 100; - reserved 101; -} - -message DurationMetric { - optional int64 id = 1; - - optional int64 what = 2; - - optional int64 condition = 3; - - repeated int64 slice_by_state = 9; - - repeated MetricConditionLink links = 4; - - repeated MetricStateLink state_link = 10; - - enum AggregationType { - SUM = 1; - - MAX_SPARSE = 2; - } - optional AggregationType aggregation_type = 5 [default = SUM]; - - optional FieldMatcher dimensions_in_what = 6; - - optional TimeUnit bucket = 7; - - optional FieldMatcher dimensions_in_condition = 8 [deprecated = true]; - - reserved 100; - reserved 101; -} - -message GaugeMetric { - optional int64 id = 1; - - optional int64 what = 2; - - optional int64 trigger_event = 12; - - optional FieldFilter gauge_fields_filter = 3; - - optional int64 condition = 4; - - optional FieldMatcher dimensions_in_what = 5; - - optional FieldMatcher dimensions_in_condition = 8 [deprecated = true]; - - optional TimeUnit bucket = 6; - - repeated MetricConditionLink links = 7; - - enum SamplingType { - RANDOM_ONE_SAMPLE = 1; - ALL_CONDITION_CHANGES = 2 [deprecated = true]; - CONDITION_CHANGE_TO_TRUE = 3; - FIRST_N_SAMPLES = 4; - } - optional SamplingType sampling_type = 9 [default = RANDOM_ONE_SAMPLE] ; - - optional int64 min_bucket_size_nanos = 10; - - optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10]; - - optional int32 max_pull_delay_sec = 13 [default = 30]; - - optional bool split_bucket_for_app_upgrade = 14 [default = true]; - - reserved 100; - reserved 101; -} - -message ValueMetric { - optional int64 id = 1; - - optional int64 what = 2; - - optional FieldMatcher value_field = 3; - - optional int64 condition = 4; - - optional FieldMatcher dimensions_in_what = 5; - - repeated int64 slice_by_state = 18; - - optional TimeUnit bucket = 6; - - repeated MetricConditionLink links = 7; - - repeated MetricStateLink state_link = 19; - - enum AggregationType { - SUM = 1; - MIN = 2; - MAX = 3; - AVG = 4; - } - optional AggregationType aggregation_type = 8 [default = SUM]; - - optional int64 min_bucket_size_nanos = 10; - - optional bool use_absolute_value_on_reset = 11 [default = false]; - - optional bool use_diff = 12; - - optional bool use_zero_default_base = 15 [default = false]; - - enum ValueDirection { - UNKNOWN = 0; - INCREASING = 1; - DECREASING = 2; - ANY = 3; - } - optional ValueDirection value_direction = 13 [default = INCREASING]; - - optional bool skip_zero_diff_output = 14 [default = true]; - - optional int32 max_pull_delay_sec = 16 [default = 30]; - - optional bool split_bucket_for_app_upgrade = 17 [default = true]; - - optional FieldMatcher dimensions_in_condition = 9 [deprecated = true]; - - reserved 100; - reserved 101; -} - -message Alert { - optional int64 id = 1; - - optional int64 metric_id = 2; - - optional int32 num_buckets = 3; - - optional int32 refractory_period_secs = 4; - - optional double trigger_if_sum_gt = 5; -} - -message Alarm { - optional int64 id = 1; - - optional int64 offset_millis = 2; - - optional int64 period_millis = 3; -} - -message IncidentdDetails { - repeated int32 section = 1; - - enum Destination { - AUTOMATIC = 0; - EXPLICIT = 1; - } - optional Destination dest = 2; - - // Package name of the incident report receiver. - optional string receiver_pkg = 3; - - // Class name of the incident report receiver. - optional string receiver_cls = 4; - - optional string alert_description = 5; -} - -message PerfettoDetails { - // The |trace_config| field is a proto-encoded message of type - // perfetto.protos.TraceConfig defined in - // //external/perfetto/protos/perfetto/config/. On device, - // statsd doesn't need to deserialize the message as it's just - // passed binary-encoded to the perfetto cmdline client. - optional bytes trace_config = 1; -} - -message BroadcastSubscriberDetails { - optional int64 subscriber_id = 1; - repeated string cookie = 2; -} - -message Subscription { - optional int64 id = 1; - - enum RuleType { - RULE_TYPE_UNSPECIFIED = 0; - ALARM = 1; - ALERT = 2; - } - optional RuleType rule_type = 2; - - optional int64 rule_id = 3; - - oneof subscriber_information { - IncidentdDetails incidentd_details = 4; - PerfettoDetails perfetto_details = 5; - BroadcastSubscriberDetails broadcast_subscriber_details = 6; - } - - optional float probability_of_informing = 7 [default = 1.1]; - - // This was used for perfprofd historically. - reserved 8; -} - -enum ActivationType { - ACTIVATION_TYPE_UNKNOWN = 0; - ACTIVATE_IMMEDIATELY = 1; - ACTIVATE_ON_BOOT = 2; -} - -message EventActivation { - optional int64 atom_matcher_id = 1; - optional int64 ttl_seconds = 2; - optional int64 deactivation_atom_matcher_id = 3; - optional ActivationType activation_type = 4; -} - -message MetricActivation { - optional int64 metric_id = 1; - - optional ActivationType activation_type = 3 [deprecated = true]; - - repeated EventActivation event_activation = 2; -} - -message PullAtomPackages { - optional int32 atom_id = 1; - - repeated string packages = 2; -} - -message StatsdConfig { - optional int64 id = 1; - - repeated EventMetric event_metric = 2; - - repeated CountMetric count_metric = 3; - - repeated ValueMetric value_metric = 4; - - repeated GaugeMetric gauge_metric = 5; - - repeated DurationMetric duration_metric = 6; - - repeated AtomMatcher atom_matcher = 7; - - repeated Predicate predicate = 8; - - repeated Alert alert = 9; - - repeated Alarm alarm = 10; - - repeated Subscription subscription = 11; - - repeated string allowed_log_source = 12; - - repeated int64 no_report_metric = 13; - - message Annotation { - optional int64 field_int64 = 1; - optional int32 field_int32 = 2; - } - repeated Annotation annotation = 14; - - optional int64 ttl_in_seconds = 15; - - optional bool hash_strings_in_metric_report = 16 [default = true]; - - repeated MetricActivation metric_activation = 17; - - optional bool version_strings_in_metric_report = 18; - - optional bool installer_in_metric_report = 19; - - optional bool persist_locally = 20 [default = false]; - - repeated State state = 21; - - repeated string default_pull_packages = 22; - - repeated PullAtomPackages pull_atom_packages = 23; - - repeated int32 whitelisted_atom_ids = 24; - - // Field number 1000 is reserved for later use. - reserved 1000; -} diff --git a/cmds/statsd/src/statsd_metadata.proto b/cmds/statsd/src/statsd_metadata.proto deleted file mode 100644 index 200b392f7542..000000000000 --- a/cmds/statsd/src/statsd_metadata.proto +++ /dev/null @@ -1,67 +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. - */ - -syntax = "proto2"; - -package android.os.statsd.metadata; - -message ConfigKey { - optional int64 config_id = 1; - optional int32 uid = 2; -} - -message Field { - optional int32 tag = 1; - optional int32 field = 2; -} - -message FieldValue { - optional Field field = 1; - oneof value { - int32 value_int = 2; - int64 value_long = 3; - float value_float = 4; - double value_double = 5; - string value_str = 6; - bytes value_storage = 7; - } -} - -message MetricDimensionKey { - repeated FieldValue dimension_key_in_what = 1; - repeated FieldValue state_values_key = 2; -} - -message AlertDimensionKeyedData { - // The earliest time the alert can be fired again in wall clock time. - optional int32 last_refractory_ends_sec = 1; - optional MetricDimensionKey dimension_key = 2; -} - -message AlertMetadata { - optional int64 alert_id = 1; - repeated AlertDimensionKeyedData alert_dim_keyed_data = 2; -} - -// All metadata for a config in statsd -message StatsMetadata { - optional ConfigKey config_key = 1; - repeated AlertMetadata alert_metadata = 2; -} - -message StatsMetadataList { - repeated StatsMetadata stats_metadata = 1; -}
\ No newline at end of file diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp deleted file mode 100644 index dcfdfe3aae53..000000000000 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ /dev/null @@ -1,781 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "android-base/stringprintf.h" -#include "guardrail/StatsdStats.h" -#include "storage/StorageManager.h" -#include "stats_log_util.h" - -#include <android-base/file.h> -#include <private/android_filesystem_config.h> -#include <fstream> - -namespace android { -namespace os { -namespace statsd { - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_MESSAGE; -using std::map; - -/** - * NOTE: these directories are protected by SELinux, any changes here must also update - * the SELinux policies. - */ -#define STATS_DATA_DIR "/data/misc/stats-data" -#define STATS_SERVICE_DIR "/data/misc/stats-service" -#define TRAIN_INFO_DIR "/data/misc/train-info" -#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin" - -// Magic word at the start of the train info file, change this if changing the file format -const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf; - -// for ConfigMetricsReportList -const int FIELD_ID_REPORTS = 2; - -std::mutex StorageManager::sTrainInfoMutex; - -using android::base::StringPrintf; -using std::unique_ptr; - -struct FileName { - int64_t mTimestampSec; - int mUid; - int64_t mConfigId; - bool mIsHistory; - string getFullFileName(const char* path) { - return StringPrintf("%s/%lld_%d_%lld%s", path, (long long)mTimestampSec, (int)mUid, - (long long)mConfigId, (mIsHistory ? "_history" : "")); - }; -}; - -string StorageManager::getDataFileName(long wallClockSec, int uid, int64_t id) { - return StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, wallClockSec, uid, - (long long)id); -} - -string StorageManager::getDataHistoryFileName(long wallClockSec, int uid, int64_t id) { - return StringPrintf("%s/%ld_%d_%lld_history", STATS_DATA_DIR, wallClockSec, uid, - (long long)id); -} - -static string findTrainInfoFileNameLocked(const string& trainName) { - unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir); - if (dir == NULL) { - VLOG("Path %s does not exist", TRAIN_INFO_DIR); - return ""; - } - dirent* de; - while ((de = readdir(dir.get()))) { - char* fileName = de->d_name; - if (fileName[0] == '.') continue; - - size_t fileNameLength = strlen(fileName); - if (fileNameLength >= trainName.length()) { - if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(), - trainName.length())) { - return string(fileName); - } - } - } - - return ""; -} - -// Returns array of int64_t which contains timestamp in seconds, uid, -// configID and whether the file is a local history file. -static void parseFileName(char* name, FileName* output) { - int64_t result[3]; - int index = 0; - char* substr = strtok(name, "_"); - while (substr != nullptr && index < 3) { - result[index] = StrToInt64(substr); - index++; - substr = strtok(nullptr, "_"); - } - // When index ends before hitting 3, file name is corrupted. We - // intentionally put -1 at index 0 to indicate the error to caller. - // TODO(b/110563137): consider removing files with unexpected name format. - if (index < 3) { - result[0] = -1; - } - - output->mTimestampSec = result[0]; - output->mUid = result[1]; - output->mConfigId = result[2]; - // check if the file is a local history. - output->mIsHistory = (substr != nullptr && strcmp("history", substr) == 0); -} - -void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) { - int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); - if (fd == -1) { - VLOG("Attempt to access %s but failed", file); - return; - } - trimToFit(STATS_SERVICE_DIR); - trimToFit(STATS_DATA_DIR); - - if (android::base::WriteFully(fd, buffer, numBytes)) { - VLOG("Successfully wrote %s", file); - } else { - ALOGE("Failed to write %s", file); - } - - int result = fchown(fd, AID_STATSD, AID_STATSD); - if (result) { - VLOG("Failed to chown %s to statsd", file); - } - - close(fd); -} - -bool StorageManager::writeTrainInfo(const InstallTrainInfo& trainInfo) { - std::lock_guard<std::mutex> lock(sTrainInfoMutex); - - if (trainInfo.trainName.empty()) { - return false; - } - deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str()); - - std::string fileName = - StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long) getWallClockSec(), - trainInfo.trainName.c_str()); - - int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); - if (fd == -1) { - VLOG("Attempt to access %s but failed", fileName.c_str()); - return false; - } - - size_t result; - // Write the magic word - result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC)); - if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) { - VLOG("Failed to wrtie train info magic"); - close(fd); - return false; - } - - // Write the train version - const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode); - result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount); - if (result != trainVersionCodeByteCount) { - VLOG("Failed to wrtie train version code"); - close(fd); - return false; - } - - // Write # of bytes in trainName to file - const size_t trainNameSize = trainInfo.trainName.size(); - const size_t trainNameSizeByteCount = sizeof(trainNameSize); - result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount); - if (result != trainNameSizeByteCount) { - VLOG("Failed to write train name size"); - close(fd); - return false; - } - - // Write trainName to file - result = write(fd, trainInfo.trainName.c_str(), trainNameSize); - if (result != trainNameSize) { - VLOG("Failed to write train name"); - close(fd); - return false; - } - - // Write status to file - const size_t statusByteCount = sizeof(trainInfo.status); - result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount); - if (result != statusByteCount) { - VLOG("Failed to write status"); - close(fd); - return false; - } - - // Write experiment id count to file. - const size_t experimentIdsCount = trainInfo.experimentIds.size(); - const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount); - result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount); - if (result != experimentIdsCountByteCount) { - VLOG("Failed to write experiment id count"); - close(fd); - return false; - } - - // Write experimentIds to file - for (size_t i = 0; i < experimentIdsCount; i++) { - const int64_t experimentId = trainInfo.experimentIds[i]; - const size_t experimentIdByteCount = sizeof(experimentId); - result = write(fd, &experimentId, experimentIdByteCount); - if (result == experimentIdByteCount) { - VLOG("Successfully wrote experiment IDs"); - } else { - VLOG("Failed to write experiment ids"); - close(fd); - return false; - } - } - - // Write bools to file - const size_t boolByteCount = sizeof(trainInfo.requiresStaging); - result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to write requires staging"); - close(fd); - return false; - } - - result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to write rollback enabled"); - close(fd); - return false; - } - - result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to write requires log latency monitor"); - close(fd); - return false; - } - - close(fd); - return true; -} - -bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo) { - std::lock_guard<std::mutex> lock(sTrainInfoMutex); - return readTrainInfoLocked(trainName, trainInfo); -} - -bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) { - trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true); - string fileName = findTrainInfoFileNameLocked(trainName); - if (fileName.empty()) { - return false; - } - int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName.c_str()).c_str(), O_RDONLY | O_CLOEXEC); - if (fd == -1) { - VLOG("Failed to open %s", fileName.c_str()); - return false; - } - - // Read the magic word - uint32_t magic; - size_t result = read(fd, &magic, sizeof(magic)); - if (result != sizeof(magic)) { - VLOG("Failed to read train info magic"); - close(fd); - return false; - } - - if (magic != TRAIN_INFO_FILE_MAGIC) { - VLOG("Train info magic was 0x%08x, expected 0x%08x", magic, TRAIN_INFO_FILE_MAGIC); - close(fd); - return false; - } - - // Read the train version code - const size_t trainVersionCodeByteCount(sizeof(trainInfo.trainVersionCode)); - result = read(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount); - if (result != trainVersionCodeByteCount) { - VLOG("Failed to read train version code from train info file"); - close(fd); - return false; - } - - // Read # of bytes taken by trainName in the file. - size_t trainNameSize; - result = read(fd, &trainNameSize, sizeof(size_t)); - if (result != sizeof(size_t)) { - VLOG("Failed to read train name size from train info file"); - close(fd); - return false; - } - - // Read trainName - trainInfo.trainName.resize(trainNameSize); - result = read(fd, trainInfo.trainName.data(), trainNameSize); - if (result != trainNameSize) { - VLOG("Failed to read train name from train info file"); - close(fd); - return false; - } - - // Read status - const size_t statusByteCount = sizeof(trainInfo.status); - result = read(fd, &trainInfo.status, statusByteCount); - if (result != statusByteCount) { - VLOG("Failed to read train status from train info file"); - close(fd); - return false; - } - - // Read experiment ids count. - size_t experimentIdsCount; - result = read(fd, &experimentIdsCount, sizeof(size_t)); - if (result != sizeof(size_t)) { - VLOG("Failed to read train experiment id count from train info file"); - close(fd); - return false; - } - - // Read experimentIds - for (size_t i = 0; i < experimentIdsCount; i++) { - int64_t experimentId; - result = read(fd, &experimentId, sizeof(experimentId)); - if (result != sizeof(experimentId)) { - VLOG("Failed to read train experiment id from train info file"); - close(fd); - return false; - } - trainInfo.experimentIds.push_back(experimentId); - } - - // Read bools - const size_t boolByteCount = sizeof(trainInfo.requiresStaging); - result = read(fd, &trainInfo.requiresStaging, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to read requires requires staging from train info file"); - close(fd); - return false; - } - - result = read(fd, &trainInfo.rollbackEnabled, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to read requires rollback enabled from train info file"); - close(fd); - return false; - } - - result = read(fd, &trainInfo.requiresLowLatencyMonitor, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to read requires requires low latency monitor from train info file"); - close(fd); - return false; - } - - // Expect to be at EOF. - char c; - result = read(fd, &c, 1); - if (result != 0) { - VLOG("Failed to read train info from file. Did not get expected EOF."); - close(fd); - return false; - } - - VLOG("Read train info file successful"); - close(fd); - return true; -} - -vector<InstallTrainInfo> StorageManager::readAllTrainInfo() { - std::lock_guard<std::mutex> lock(sTrainInfoMutex); - vector<InstallTrainInfo> trainInfoList; - unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir); - if (dir == NULL) { - VLOG("Directory does not exist: %s", TRAIN_INFO_DIR); - return trainInfoList; - } - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') { - continue; - } - - InstallTrainInfo trainInfo; - bool readSuccess = StorageManager::readTrainInfoLocked(name, trainInfo); - if (!readSuccess) { - continue; - } - trainInfoList.push_back(trainInfo); - } - return trainInfoList; -} - -void StorageManager::deleteFile(const char* file) { - if (remove(file) != 0) { - VLOG("Attempt to delete %s but is not found", file); - } else { - VLOG("Successfully deleted %s", file); - } -} - -void StorageManager::deleteAllFiles(const char* path) { - unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir); - if (dir == NULL) { - VLOG("Directory does not exist: %s", path); - return; - } - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') continue; - deleteFile(StringPrintf("%s/%s", path, name).c_str()); - } -} - -void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) { - unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir); - if (dir == NULL) { - VLOG("Directory does not exist: %s", path); - return; - } - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') { - continue; - } - size_t nameLen = strlen(name); - size_t suffixLen = strlen(suffix); - if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) { - deleteFile(StringPrintf("%s/%s", path, name).c_str()); - } - } -} - -void StorageManager::sendBroadcast(const char* path, - const std::function<void(const ConfigKey&)>& sendBroadcast) { - unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir); - if (dir == NULL) { - VLOG("no stats-data directory on disk"); - return; - } - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') continue; - VLOG("file %s", name); - - FileName output; - parseFileName(name, &output); - if (output.mTimestampSec == -1 || output.mIsHistory) continue; - sendBroadcast(ConfigKey((int)output.mUid, output.mConfigId)); - } -} - -bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) { - unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir); - if (dir == NULL) { - VLOG("Path %s does not exist", STATS_DATA_DIR); - return false; - } - - string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()); - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') continue; - - size_t nameLen = strlen(name); - size_t suffixLen = suffix.length(); - if (suffixLen <= nameLen && - strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) { - // Check again that the file name is parseable. - FileName output; - parseFileName(name, &output); - if (output.mTimestampSec == -1 || output.mIsHistory) continue; - return true; - } - } - return false; -} - -void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto, - bool erase_data, bool isAdb) { - unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir); - if (dir == NULL) { - VLOG("Path %s does not exist", STATS_DATA_DIR); - return; - } - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - string fileName(name); - if (name[0] == '.') continue; - FileName output; - parseFileName(name, &output); - - if (output.mTimestampSec == -1 || (output.mIsHistory && !isAdb) || - output.mUid != key.GetUid() || output.mConfigId != key.GetId()) { - continue; - } - - auto fullPathName = StringPrintf("%s/%s", STATS_DATA_DIR, fileName.c_str()); - int fd = open(fullPathName.c_str(), O_RDONLY | O_CLOEXEC); - if (fd != -1) { - string content; - if (android::base::ReadFdToString(fd, &content)) { - proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS, - content.c_str(), content.size()); - } - close(fd); - } else { - ALOGE("file cannot be opened"); - } - - if (erase_data) { - remove(fullPathName.c_str()); - } else if (!output.mIsHistory && !isAdb) { - // This means a real data owner has called to get this data. But the config says it - // wants to keep a local history. So now this file must be renamed as a history file. - // So that next time, when owner calls getData() again, this data won't be uploaded - // again. rename returns 0 on success - if (rename(fullPathName.c_str(), (fullPathName + "_history").c_str())) { - ALOGE("Failed to rename file %s", fullPathName.c_str()); - } - } - } -} - -bool StorageManager::readFileToString(const char* file, string* content) { - int fd = open(file, O_RDONLY | O_CLOEXEC); - bool res = false; - if (fd != -1) { - if (android::base::ReadFdToString(fd, content)) { - res = true; - } else { - VLOG("Failed to read file %s\n", file); - } - close(fd); - } - return res; -} - -void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) { - unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir); - if (dir == NULL) { - VLOG("no default config on disk"); - return; - } - trimToFit(STATS_SERVICE_DIR); - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') continue; - - FileName output; - parseFileName(name, &output); - if (output.mTimestampSec == -1) continue; - string file_name = output.getFullFileName(STATS_SERVICE_DIR); - int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); - if (fd != -1) { - string content; - if (android::base::ReadFdToString(fd, &content)) { - StatsdConfig config; - if (config.ParseFromString(content)) { - configsMap[ConfigKey(output.mUid, output.mConfigId)] = config; - VLOG("map key uid=%lld|configID=%lld", (long long)output.mUid, - (long long)output.mConfigId); - } - } - close(fd); - } - } -} - -bool StorageManager::readConfigFromDisk(const ConfigKey& key, StatsdConfig* config) { - string content; - return config != nullptr && - StorageManager::readConfigFromDisk(key, &content) && config->ParseFromString(content); -} - -bool StorageManager::readConfigFromDisk(const ConfigKey& key, string* content) { - unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), - closedir); - if (dir == NULL) { - VLOG("Directory does not exist: %s", STATS_SERVICE_DIR); - return false; - } - - string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()); - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') { - continue; - } - size_t nameLen = strlen(name); - size_t suffixLen = suffix.length(); - // There can be at most one file that matches this suffix (config key). - if (suffixLen <= nameLen && - strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) { - int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(), - O_RDONLY | O_CLOEXEC); - if (fd != -1) { - if (android::base::ReadFdToString(fd, content)) { - return true; - } - close(fd); - } - } - } - return false; -} - -bool StorageManager::hasIdenticalConfig(const ConfigKey& key, - const vector<uint8_t>& config) { - string content; - if (StorageManager::readConfigFromDisk(key, &content)) { - vector<uint8_t> vec(content.begin(), content.end()); - if (vec == config) { - return true; - } - } - return false; -} - -void StorageManager::sortFiles(vector<FileInfo>* fileNames) { - // Reverse sort to effectively remove from the back (oldest entries). - // This will sort files in reverse-chronological order. Local history files have lower - // priority than regular data files. - sort(fileNames->begin(), fileNames->end(), [](FileInfo& lhs, FileInfo& rhs) { - // first consider if the file is a local history - if (lhs.mIsHistory && !rhs.mIsHistory) { - return false; - } else if (rhs.mIsHistory && !lhs.mIsHistory) { - return true; - } - - // then consider the age. - if (lhs.mFileAgeSec < rhs.mFileAgeSec) { - return true; - } else if (lhs.mFileAgeSec > rhs.mFileAgeSec) { - return false; - } - - // then good luck.... use string::compare - return lhs.mFileName.compare(rhs.mFileName) > 0; - }); -} - -void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) { - unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir); - if (dir == NULL) { - VLOG("Path %s does not exist", path); - return; - } - dirent* de; - int totalFileSize = 0; - vector<FileInfo> fileNames; - auto nowSec = getWallClockSec(); - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') continue; - - FileName output; - string file_name; - if (parseTimestampOnly) { - file_name = StringPrintf("%s/%s", path, name); - output.mTimestampSec = StrToInt64(strtok(name, "_")); - output.mIsHistory = false; - } else { - parseFileName(name, &output); - file_name = output.getFullFileName(path); - } - if (output.mTimestampSec == -1) continue; - - // Check for timestamp and delete if it's too old. - long fileAge = nowSec - output.mTimestampSec; - if (fileAge > StatsdStats::kMaxAgeSecond || - (output.mIsHistory && fileAge > StatsdStats::kMaxLocalHistoryAgeSecond)) { - deleteFile(file_name.c_str()); - continue; - } - - ifstream file(file_name.c_str(), ifstream::in | ifstream::binary); - int fileSize = 0; - if (file.is_open()) { - file.seekg(0, ios::end); - fileSize = file.tellg(); - file.close(); - totalFileSize += fileSize; - } - fileNames.emplace_back(file_name, output.mIsHistory, fileSize, fileAge); - } - - if (fileNames.size() > StatsdStats::kMaxFileNumber || - totalFileSize > StatsdStats::kMaxFileSize) { - sortFiles(&fileNames); - } - - // Start removing files from oldest to be under the limit. - while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber || - totalFileSize > StatsdStats::kMaxFileSize)) { - totalFileSize -= fileNames.at(fileNames.size() - 1).mFileSizeBytes; - deleteFile(fileNames.at(fileNames.size() - 1).mFileName.c_str()); - fileNames.pop_back(); - } -} - -void StorageManager::printStats(int outFd) { - printDirStats(outFd, STATS_SERVICE_DIR); - printDirStats(outFd, STATS_DATA_DIR); -} - -void StorageManager::printDirStats(int outFd, const char* path) { - dprintf(outFd, "Printing stats of %s\n", path); - unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir); - if (dir == NULL) { - VLOG("Path %s does not exist", path); - return; - } - dirent* de; - int fileCount = 0; - int totalFileSize = 0; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') { - continue; - } - FileName output; - parseFileName(name, &output); - if (output.mTimestampSec == -1) continue; - dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld, %s", fileCount + 1, - (long long)output.mTimestampSec, output.mUid, (long long)output.mConfigId, - (output.mIsHistory ? "local history" : "")); - string file_name = output.getFullFileName(path); - ifstream file(file_name.c_str(), ifstream::in | ifstream::binary); - if (file.is_open()) { - file.seekg(0, ios::end); - int fileSize = file.tellg(); - file.close(); - dprintf(outFd, ", File Size: %d bytes", fileSize); - totalFileSize += fileSize; - } - dprintf(outFd, "\n"); - fileCount++; - } - dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount, - totalFileSize); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h deleted file mode 100644 index d59046dfbb99..000000000000 --- a/cmds/statsd/src/storage/StorageManager.h +++ /dev/null @@ -1,168 +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. - */ - -#ifndef STORAGE_MANAGER_H -#define STORAGE_MANAGER_H - -#include <android/util/ProtoOutputStream.h> -#include <utils/Log.h> -#include <utils/RefBase.h> - -#include "packages/UidMap.h" - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoOutputStream; - -class StorageManager : public virtual RefBase { -public: - struct FileInfo { - FileInfo(std::string name, bool isHistory, int fileSize, long fileAge) - : mFileName(name), - mIsHistory(isHistory), - mFileSizeBytes(fileSize), - mFileAgeSec(fileAge) { - } - std::string mFileName; - bool mIsHistory; - int mFileSizeBytes; - long mFileAgeSec; - }; - - /** - * Writes a given byte array as a file to the specified file path. - */ - static void writeFile(const char* file, const void* buffer, int numBytes); - - /** - * Writes train info. - */ - static bool writeTrainInfo(const InstallTrainInfo& trainInfo); - - /** - * Reads train info. - */ - static bool readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo); - - /** - * Reads train info assuming lock is obtained. - */ - static bool readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo); - - /** - * Reads all train info and returns a vector of train info. - */ - static vector<InstallTrainInfo> readAllTrainInfo(); - - /** - * Reads the file content to the buffer. - */ - static bool readFileToString(const char* file, string* content); - - /** - * Deletes a single file given a file name. - */ - static void deleteFile(const char* file); - - /** - * Deletes all files in a given directory. - */ - static void deleteAllFiles(const char* path); - - /** - * Deletes all files whose name matches with a provided suffix. - */ - static void deleteSuffixedFiles(const char* path, const char* suffix); - - /** - * Send broadcasts to relevant receiver for each data stored on disk. - */ - static void sendBroadcast(const char* path, - const std::function<void(const ConfigKey&)>& sendBroadcast); - - /** - * Returns true if there's at least one report on disk. - */ - static bool hasConfigMetricsReport(const ConfigKey& key); - - /** - * Appends the ConfigMetricsReport found on disk to the specifid proto - * and, if erase_data, deletes it from disk. - * - * [isAdb]: if the caller is adb dump. This includes local adb dump or dumpsys by - * bugreport or incidentd. When true, we will append any local history data too. - * - * When - * erase_data=true, isAdb=true: append history data to output, remove all data after read - * erase_data=false, isAdb=true: append history data to output, keep data after read - * erase_data=true, isAdb=false: do not append history data, and remove data after read - * erase_data=false, isAdb=false: do not append history data and *rename* all data files to - * history files. - */ - static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto, - bool erase_data, bool isAdb); - - /** - * Call to load the saved configs from disk. - */ - static void readConfigFromDisk(std::map<ConfigKey, StatsdConfig>& configsMap); - - /** - * Call to load the specified config from disk. Returns false if the config file does not - * exist or error occurs when reading the file. - */ - static bool readConfigFromDisk(const ConfigKey& key, StatsdConfig* config); - static bool readConfigFromDisk(const ConfigKey& key, string* config); - - /** - * Trims files in the provided directory to limit the total size, number of - * files, accumulation of outdated files. - */ - static void trimToFit(const char* dir, bool parseTimestampOnly = false); - - /** - * Returns true if there already exists identical configuration on device. - */ - static bool hasIdenticalConfig(const ConfigKey& key, - const vector<uint8_t>& config); - - /** - * Prints disk usage statistics related to statsd. - */ - static void printStats(int out); - - static string getDataFileName(long wallClockSec, int uid, int64_t id); - - static string getDataHistoryFileName(long wallClockSec, int uid, int64_t id); - - static void sortFiles(vector<FileInfo>* fileNames); - -private: - /** - * Prints disk usage statistics about a directory related to statsd. - */ - static void printDirStats(int out, const char* path); - - static std::mutex sTrainInfoMutex; -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // STORAGE_MANAGER_H diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp deleted file mode 100644 index 1d77513d9d33..000000000000 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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. - */ -#define DEBUG false -#include "Log.h" - -#include "FieldValue.h" -#include "IncidentdReporter.h" -#include "packages/UidMap.h" -#include "stats_log_util.h" - -#include <android/util/ProtoOutputStream.h> -#include <incident/incident_report.h> - -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoOutputStream; -using std::vector; - -using util::FIELD_TYPE_INT32; -using util::FIELD_TYPE_INT64; -using util::FIELD_TYPE_MESSAGE; -using util::FIELD_TYPE_STRING; - -// field ids in IncidentHeaderProto -const int FIELD_ID_ALERT_ID = 1; -const int FIELD_ID_REASON = 2; -const int FIELD_ID_CONFIG_KEY = 3; -const int FIELD_ID_CONFIG_KEY_UID = 1; -const int FIELD_ID_CONFIG_KEY_ID = 2; - -const int FIELD_ID_TRIGGER_DETAILS = 4; -const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1; -const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1; -const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2; -const int FIELD_ID_METRIC_VALUE_VALUE = 4; - -const int FIELD_ID_PACKAGE_INFO = 3; - -namespace { -void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey, - int64_t metricValue, const ConfigKey& configKey, const string& reason, - vector<uint8_t>* protoData) { - ProtoOutputStream headerProto; - headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id); - headerProto.write(FIELD_TYPE_STRING | FIELD_ID_REASON, reason); - uint64_t token = - headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY); - headerProto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_KEY_UID, configKey.GetUid()); - headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId()); - headerProto.end(token); - - token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS); - - // MetricValue trigger_metric = 1; - uint64_t metricToken = - headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC); - // message MetricValue { - // optional int64 metric_id = 1; - headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_METRIC_ID, (long long)metricId); - // optional DimensionsValue dimension_in_what = 2; - uint64_t dimToken = - headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT); - writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto); - headerProto.end(dimToken); - - // deprecated field - // optional DimensionsValue dimension_in_condition = 3; - - // optional int64 value = 4; - headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue); - - // } - headerProto.end(metricToken); - - // write relevant uid package info - std::set<int32_t> uids; - - for (const auto& dim : dimensionKey.getDimensionKeyInWhat().getValues()) { - int uid = getUidIfExists(dim); - // any uid <= 2000 are predefined AID_* - if (uid > 2000) { - uids.insert(uid); - } - } - - if (!uids.empty()) { - uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO); - UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids, - nullptr /*string set*/, &headerProto); - headerProto.end(token); - } - - headerProto.end(token); - - protoData->resize(headerProto.size()); - size_t pos = 0; - sp<android::util::ProtoReader> reader = headerProto.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((*protoData)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } -} -} // namespace - -bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId, - const MetricDimensionKey& dimensionKey, int64_t metricValue, - const ConfigKey& configKey) { - if (config.section_size() == 0) { - VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id, - configKey.GetUid(), (long long)configKey.GetId()); - return false; - } - - AIncidentReportArgs* args = AIncidentReportArgs_init(); - - vector<uint8_t> protoData; - getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, - config.alert_description(), &protoData); - AIncidentReportArgs_addHeader(args, protoData.data(), protoData.size()); - - for (int i = 0; i < config.section_size(); i++) { - AIncidentReportArgs_addSection(args, config.section(i)); - } - - uint8_t dest; - switch (config.dest()) { - case IncidentdDetails_Destination_AUTOMATIC: - dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC; - break; - case IncidentdDetails_Destination_EXPLICIT: - dest = INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT; - break; - default: - dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC; - } - AIncidentReportArgs_setPrivacyPolicy(args, dest); - - AIncidentReportArgs_setReceiverPackage(args, config.receiver_pkg().c_str()); - - AIncidentReportArgs_setReceiverClass(args, config.receiver_cls().c_str()); - - int err = AIncidentReportArgs_takeReport(args); - AIncidentReportArgs_delete(args); - - return err == NO_ERROR; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h deleted file mode 100644 index e78a4d98dcd8..000000000000 --- a/cmds/statsd/src/subscriber/IncidentdReporter.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include "HashableDimensionKey.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert, IncidentdDetails - -namespace android { -namespace os { -namespace statsd { - -/** - * Calls incidentd to trigger an incident report and put in dropbox for uploading. - */ -bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId, - const MetricDimensionKey& dimensionKey, int64_t metricValue, - const ConfigKey& configKey); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp deleted file mode 100644 index c915ef3bf069..000000000000 --- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "SubscriberReporter.h" - -using std::lock_guard; - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -struct BroadcastSubscriberDeathCookie { - BroadcastSubscriberDeathCookie(const ConfigKey& configKey, int64_t subscriberId, - const shared_ptr<IPendingIntentRef>& pir): - mConfigKey(configKey), - mSubscriberId(subscriberId), - mPir(pir) {} - - ConfigKey mConfigKey; - int64_t mSubscriberId; - shared_ptr<IPendingIntentRef> mPir; -}; - -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; - - 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_; -} - -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; - } - AIBinder_linkToDeath(pir->asBinder().get(), mBroadcastSubscriberDeathRecipient.get(), - new BroadcastSubscriberDeathCookie(configKey, subscriberId, pir)); -} - -void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey, - int64_t subscriberId) { - VLOG("SubscriberReporter::unsetBroadcastSubscriber called."); - lock_guard<mutex> lock(mLock); - auto subscriberMapIt = mIntentMap.find(configKey); - if (subscriberMapIt != mIntentMap.end()) { - subscriberMapIt->second.erase(subscriberId); - if (subscriberMapIt->second.empty()) { - mIntentMap.erase(configKey); - } - } -} - -void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey, - const Subscription& subscription, - const MetricDimensionKey& dimKey) const { - // Reminder about ids: - // subscription id - name of the Subscription (that ties the Alert to the broadcast) - // subscription rule_id - the name of the Alert (that triggers the broadcast) - // subscriber_id - name of the PendingIntent to use to send the broadcast - // config uid - the uid that uploaded the config (and therefore gave the PendingIntent, - // although the intent may be to broadcast to a different uid) - // config id - the name of this config (for this particular uid) - - VLOG("SubscriberReporter::alertBroadcastSubscriber called."); - lock_guard<mutex> lock(mLock); - - if (!subscription.has_broadcast_subscriber_details() - || !subscription.broadcast_subscriber_details().has_subscriber_id()) { - ALOGE("Broadcast subscriber does not have an id."); - return; - } - int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id(); - - vector<string> cookies; - cookies.reserve(subscription.broadcast_subscriber_details().cookie_size()); - for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) { - cookies.push_back(cookie); - } - - auto it1 = mIntentMap.find(configKey); - if (it1 == mIntentMap.end()) { - ALOGW("Cannot inform subscriber for missing config key %s ", configKey.ToString().c_str()); - return; - } - auto it2 = it1->second.find(subscriberId); - if (it2 == it1->second.end()) { - ALOGW("Cannot inform subscriber of config %s for missing subscriberId %lld ", - configKey.ToString().c_str(), (long long)subscriberId); - return; - } - sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey); -} - -void SubscriberReporter::sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir, - const ConfigKey& configKey, - const Subscription& subscription, - const vector<string>& cookies, - const MetricDimensionKey& dimKey) const { - VLOG("SubscriberReporter::sendBroadcastLocked called."); - pir->sendSubscriberBroadcast( - configKey.GetUid(), - configKey.GetId(), - subscription.id(), - subscription.rule_id(), - cookies, - dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel()); -} - -shared_ptr<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey, - int64_t subscriberId) { - lock_guard<mutex> lock(mLock); - auto subscriberMapIt = mIntentMap.find(configKey); - if (subscriberMapIt == mIntentMap.end()) { - return nullptr; - } - auto pirMapIt = subscriberMapIt->second.find(subscriberId); - if (pirMapIt == subscriberMapIt->second.end()) { - return nullptr; - } - return pirMapIt->second; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h deleted file mode 100644 index 4fe428198e71..000000000000 --- a/cmds/statsd/src/subscriber/SubscriberReporter.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include <aidl/android/os/IPendingIntentRef.h> -#include <utils/RefBase.h> -#include <utils/String16.h> - -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // subscription -#include "HashableDimensionKey.h" - -#include <mutex> -#include <unordered_map> -#include <vector> - -using aidl::android::os::IPendingIntentRef; -using std::mutex; -using std::shared_ptr; -using std::string; -using std::unordered_map; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -// Reports information to subscribers. -// Single instance shared across the process. All methods are thread safe. -class SubscriberReporter { -public: - /** Get (singleton) instance of SubscriberReporter. */ - static SubscriberReporter& getInstance() { - static SubscriberReporter subscriberReporter; - return subscriberReporter; - } - - ~SubscriberReporter(){}; - SubscriberReporter(SubscriberReporter const&) = delete; - void operator=(SubscriberReporter const&) = delete; - - /** - * Stores the given intentSender, associating it with the given (configKey, subscriberId) pair. - */ - void setBroadcastSubscriber(const ConfigKey& configKey, - int64_t subscriberId, - const shared_ptr<IPendingIntentRef>& pir); - - /** - * Erases any intentSender information from the given (configKey, subscriberId) pair. - */ - void unsetBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId); - - /** - * Sends a broadcast via the intentSender previously stored for the - * given (configKey, subscriberId) pair by setBroadcastSubscriber. - * Information about the subscriber, as well as information extracted from the dimKey, is sent. - */ - void alertBroadcastSubscriber(const ConfigKey& configKey, - const Subscription& subscription, - const MetricDimensionKey& dimKey) const; - - shared_ptr<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey, - int64_t subscriberId); - -private: - SubscriberReporter(); - - mutable mutex mLock; - - /** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */ - unordered_map<ConfigKey, unordered_map<int64_t, shared_ptr<IPendingIntentRef>>> mIntentMap; - - /** - * Sends a broadcast via the given intentSender (using mStatsCompanionService), along - * with the information in the other parameters. - */ - void sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir, - const ConfigKey& configKey, - 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 -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/uid_data.proto b/cmds/statsd/src/uid_data.proto deleted file mode 100644 index a6fa26cd412e..000000000000 --- a/cmds/statsd/src/uid_data.proto +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.internal.os"; -option java_outer_classname = "UidDataProto"; - -message ApplicationInfo { - optional int32 uid = 1; - optional int64 version = 2; - optional string version_string = 3; - optional string package_name = 4; - optional string installer = 5; -} - -// StatsServiceCompanion uses the proto to supply statsd with uid-package -// mapping updates. -message UidData { - repeated ApplicationInfo app_info = 1; -} diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.cpp b/cmds/statsd/src/utils/MultiConditionTrigger.cpp deleted file mode 100644 index 43a69337f368..000000000000 --- a/cmds/statsd/src/utils/MultiConditionTrigger.cpp +++ /dev/null @@ -1,57 +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. - */ -#define DEBUG false // STOPSHIP if true - -#include "MultiConditionTrigger.h" - -#include <thread> - -using namespace std; - -namespace android { -namespace os { -namespace statsd { - -MultiConditionTrigger::MultiConditionTrigger(const set<string>& conditionNames, - function<void()> trigger) - : mRemainingConditionNames(conditionNames), - mTrigger(trigger), - mCompleted(mRemainingConditionNames.empty()) { - if (mCompleted) { - thread executorThread([this] { mTrigger(); }); - executorThread.detach(); - } -} - -void MultiConditionTrigger::markComplete(const string& conditionName) { - bool doTrigger = false; - { - lock_guard<mutex> lg(mMutex); - if (mCompleted) { - return; - } - mRemainingConditionNames.erase(conditionName); - mCompleted = mRemainingConditionNames.empty(); - doTrigger = mCompleted; - } - if (doTrigger) { - std::thread executorThread([this] { mTrigger(); }); - executorThread.detach(); - } -} -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.h b/cmds/statsd/src/utils/MultiConditionTrigger.h deleted file mode 100644 index 51f6029915be..000000000000 --- a/cmds/statsd/src/utils/MultiConditionTrigger.h +++ /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. - */ -#pragma once - -#include <gtest/gtest_prod.h> - -#include <mutex> -#include <set> - -namespace android { -namespace os { -namespace statsd { - -/** - * This class provides a utility to wait for a set of named conditions to occur. - * - * It will execute the trigger runnable in a detached thread once all conditions have been marked - * true. - */ -class MultiConditionTrigger { -public: - explicit MultiConditionTrigger(const std::set<std::string>& conditionNames, - std::function<void()> trigger); - - MultiConditionTrigger(const MultiConditionTrigger&) = delete; - MultiConditionTrigger& operator=(const MultiConditionTrigger&) = delete; - - // Mark a specific condition as true. If this condition has called markComplete already or if - // the event was not specified in the constructor, the function is a no-op. - void markComplete(const std::string& eventName); - -private: - mutable std::mutex mMutex; - std::set<std::string> mRemainingConditionNames; - std::function<void()> mTrigger; - bool mCompleted; - - FRIEND_TEST(MultiConditionTriggerTest, TestCountDownCalledBySameEventName); -}; -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/statsd_test.xml b/cmds/statsd/statsd_test.xml deleted file mode 100644 index 8f9bb1cb6b2a..000000000000 --- a/cmds/statsd/statsd_test.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Runs statsd_test."> - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="apct-native" /> - <option name="test-suite-tag" value="mts" /> - - <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> - - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> - <option name="cleanup" value="true" /> - <option name="push" value="statsd_test->/data/local/tmp/statsd_test" /> - <option name="append-bitness" value="true" /> - </target_preparer> - - <test class="com.android.tradefed.testtype.GTest" > - <option name="native-test-device-path" value="/data/local/tmp" /> - <option name="module-name" value="statsd_test" /> - </test> - - <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> - <option name="mainline-module-package-name" value="com.google.android.os.statsd" /> - </object> -</configuration> diff --git a/cmds/statsd/tests/AlarmMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp deleted file mode 100644 index 1dc9795dcf16..000000000000 --- a/cmds/statsd/tests/AlarmMonitor_test.cpp +++ /dev/null @@ -1,69 +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. - -#include "anomaly/AlarmMonitor.h" - -#include <gtest/gtest.h> - -using namespace android::os::statsd; -using std::shared_ptr; - -#ifdef __ANDROID__ -TEST(AlarmMonitor, popSoonerThan) { - std::string emptyMetricId; - std::string emptyDimensionId; - unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> set; - AlarmMonitor am(2, - [](const shared_ptr<IStatsCompanionService>&, int64_t){}, - [](const shared_ptr<IStatsCompanionService>&){}); - - set = am.popSoonerThan(5); - EXPECT_TRUE(set.empty()); - - sp<const InternalAlarm> a = new InternalAlarm{10}; - sp<const InternalAlarm> b = new InternalAlarm{20}; - sp<const InternalAlarm> c = new InternalAlarm{20}; - sp<const InternalAlarm> d = new InternalAlarm{30}; - sp<const InternalAlarm> e = new InternalAlarm{40}; - sp<const InternalAlarm> f = new InternalAlarm{50}; - - am.add(a); - am.add(b); - am.add(c); - am.add(d); - am.add(e); - am.add(f); - - set = am.popSoonerThan(5); - EXPECT_TRUE(set.empty()); - - set = am.popSoonerThan(30); - ASSERT_EQ(4u, set.size()); - EXPECT_EQ(1u, set.count(a)); - EXPECT_EQ(1u, set.count(b)); - EXPECT_EQ(1u, set.count(c)); - EXPECT_EQ(1u, set.count(d)); - - set = am.popSoonerThan(60); - ASSERT_EQ(2u, set.size()); - EXPECT_EQ(1u, set.count(e)); - EXPECT_EQ(1u, set.count(f)); - - set = am.popSoonerThan(80); - ASSERT_EQ(0u, set.size()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp deleted file mode 100644 index 1d8371638e90..000000000000 --- a/cmds/statsd/tests/ConfigManager_test.cpp +++ /dev/null @@ -1,159 +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. - -#include "src/config/ConfigManager.h" -#include "src/metrics/MetricsManager.h" -#include "statsd_test_util.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <stdio.h> -#include <iostream> - -using namespace android; -using namespace android::os::statsd; -using namespace testing; -using namespace std; - -namespace android { -namespace os { -namespace statsd { - -static ostream& operator<<(ostream& os, const StatsdConfig& config) { - return os << "StatsdConfig{id=" << config.id() << "}"; -} - -} // namespace statsd -} // namespace os -} // namespace android - -/** - * Mock ConfigListener - */ -class MockListener : public ConfigListener { -public: - MOCK_METHOD4(OnConfigUpdated, void(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config, bool modularUpdate)); - MOCK_METHOD1(OnConfigRemoved, void(const ConfigKey& key)); -}; - -/** - * Validate that the ConfigKey is the one we wanted. - */ -MATCHER_P2(ConfigKeyEq, uid, id, "") { - return arg.GetUid() == uid && (long long)arg.GetId() == (long long)id; -} - -/** - * Validate that the StatsdConfig is the one we wanted. - */ -MATCHER_P(StatsdConfigEq, id, 0) { - return (long long)arg.id() == (long long)id; -} - -const int64_t testConfigId = 12345; - -/** - * Test the addOrUpdate and remove methods - */ -TEST(ConfigManagerTest, TestAddUpdateRemove) { - sp<MockListener> listener = new StrictMock<MockListener>(); - - sp<ConfigManager> manager = new ConfigManager(); - manager->AddListener(listener); - - StatsdConfig config91; - config91.set_id(91); - StatsdConfig config92; - config92.set_id(92); - StatsdConfig config93; - config93.set_id(93); - StatsdConfig config94; - config94.set_id(94); - - { - InSequence s; - - manager->StartupForTest(); - - // Add another one - EXPECT_CALL(*(listener.get()), - OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(91), _)) - .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config91); - - // Update It - EXPECT_CALL(*(listener.get()), - OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(92), _)) - .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config92); - - // Add one with the same uid but a different name - EXPECT_CALL(*(listener.get()), - OnConfigUpdated(_, ConfigKeyEq(1, StringToId("yyy")), StatsdConfigEq(93), _)) - .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(1, StringToId("yyy")), config93); - - // Add one with the same name but a different uid - EXPECT_CALL(*(listener.get()), - OnConfigUpdated(_, ConfigKeyEq(2, StringToId("zzz")), StatsdConfigEq(94), _)) - .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config94); - - // Remove (1,yyy) - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("yyy")))) - .RetiresOnSaturation(); - manager->RemoveConfig(ConfigKey(1, StringToId("yyy"))); - - // Remove (2,zzz) - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz")))) - .RetiresOnSaturation(); - manager->RemoveConfig(ConfigKey(2, StringToId("zzz"))); - - // Remove (1,zzz) - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("zzz")))) - .RetiresOnSaturation(); - manager->RemoveConfig(ConfigKey(1, StringToId("zzz"))); - - // Remove (2,zzz) again and we shouldn't get the callback - manager->RemoveConfig(ConfigKey(2, StringToId("zzz"))); - } -} - -/** - * Test removing all of the configs for a uid. - */ -TEST(ConfigManagerTest, TestRemoveUid) { - sp<MockListener> listener = new StrictMock<MockListener>(); - - sp<ConfigManager> manager = new ConfigManager(); - manager->AddListener(listener); - - StatsdConfig config; - - EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _, _, _)).Times(5); - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("xxx")))); - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("yyy")))); - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz")))); - - manager->StartupForTest(); - manager->UpdateConfig(ConfigKey(1, StringToId("aaa")), config); - manager->UpdateConfig(ConfigKey(2, StringToId("xxx")), config); - manager->UpdateConfig(ConfigKey(2, StringToId("yyy")), config); - manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config); - manager->UpdateConfig(ConfigKey(3, StringToId("bbb")), config); - - manager->RemoveConfigs(2); -} diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp deleted file mode 100644 index a21eb9b9147f..000000000000 --- a/cmds/statsd/tests/FieldValue_test.cpp +++ /dev/null @@ -1,659 +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. - */ -#include <gtest/gtest.h> - -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/matcher_util.h" -#include "src/logd/LogEvent.h" -#include "stats_event.h" -#include "stats_log_util.h" -#include "stats_util.h" -#include "subscriber/SubscriberReporter.h" -#include "tests/statsd_test_util.h" - -#ifdef __ANDROID__ - -using android::util::ProtoReader; - -namespace android { -namespace os { -namespace statsd { - -// These constants must be kept in sync with those in StatsDimensionsValue.java. -const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2; -const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3; -const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6; -const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7; - -namespace { -void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const vector<int>& attributionUids, const vector<string>& attributionTags, - const string& name) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, name.c_str()); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const vector<int>& attributionUids, const vector<string>& attributionTags, - const int32_t value) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeInt32(statsEvent, value); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} -} // anonymous namespace - -TEST(AtomMatcherTest, TestFieldTranslation) { - FieldMatcher matcher1; - matcher1.set_field(10); - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - child->set_position(Position::ANY); - - child = child->add_child(); - child->set_field(1); - - vector<Matcher> output; - translateFieldMatcher(matcher1, &output); - - ASSERT_EQ((size_t)1, output.size()); - - const auto& matcher12 = output[0]; - EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag()); - EXPECT_EQ((int32_t)0x02010001, matcher12.mMatcher.getField()); - EXPECT_EQ((int32_t)0xff7f007f, matcher12.mMask); -} - -TEST(AtomMatcherTest, TestFieldTranslation_ALL) { - FieldMatcher matcher1; - matcher1.set_field(10); - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - child->set_position(Position::ALL); - - child = child->add_child(); - child->set_field(1); - - vector<Matcher> output; - translateFieldMatcher(matcher1, &output); - - ASSERT_EQ((size_t)1, output.size()); - - const auto& matcher12 = output[0]; - EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag()); - EXPECT_EQ((int32_t)0x02010001, matcher12.mMatcher.getField()); - EXPECT_EQ((int32_t)0xff7f7f7f, matcher12.mMask); -} - -TEST(AtomMatcherTest, TestFilter_ALL) { - FieldMatcher matcher1; - matcher1.set_field(10); - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - child->set_position(Position::ALL); - - child->add_child()->set_field(1); - child->add_child()->set_field(2); - - child = matcher1.add_child(); - child->set_field(2); - - vector<Matcher> matchers; - translateFieldMatcher(matcher1, &matchers); - - std::vector<int> attributionUids = {1111, 2222, 3333}; - std::vector<string> attributionTags = {"location1", "location2", "location3"}; - - LogEvent event(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value"); - HashableDimensionKey output; - - filterValues(matchers, event.getValues(), &output); - - ASSERT_EQ((size_t)7, output.getValues().size()); - EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField()); - EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value); - EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField()); - EXPECT_EQ("location1", output.getValues()[1].mValue.str_value); - - EXPECT_EQ((int32_t)0x02010201, output.getValues()[2].mField.getField()); - EXPECT_EQ((int32_t)2222, output.getValues()[2].mValue.int_value); - EXPECT_EQ((int32_t)0x02010202, output.getValues()[3].mField.getField()); - EXPECT_EQ("location2", output.getValues()[3].mValue.str_value); - - EXPECT_EQ((int32_t)0x02010301, output.getValues()[4].mField.getField()); - EXPECT_EQ((int32_t)3333, output.getValues()[4].mValue.int_value); - EXPECT_EQ((int32_t)0x02010302, output.getValues()[5].mField.getField()); - EXPECT_EQ("location3", output.getValues()[5].mValue.str_value); - - EXPECT_EQ((int32_t)0x00020000, output.getValues()[6].mField.getField()); - EXPECT_EQ("some value", output.getValues()[6].mValue.str_value); -} - -TEST(AtomMatcherTest, TestSubDimension) { - HashableDimensionKey dim; - - int pos1[] = {1, 1, 1}; - int pos2[] = {1, 1, 2}; - int pos3[] = {1, 1, 3}; - int pos4[] = {2, 0, 0}; - Field field1(10, pos1, 2); - Field field2(10, pos2, 2); - - Field field3(10, pos3, 2); - Field field4(10, pos4, 0); - - Value value1((int32_t)10025); - Value value2("tag"); - - Value value11((int32_t)10026); - Value value22("tag2"); - - dim.addValue(FieldValue(field1, value1)); - dim.addValue(FieldValue(field2, value2)); - - HashableDimensionKey subDim1; - subDim1.addValue(FieldValue(field1, value1)); - - HashableDimensionKey subDim2; - subDim1.addValue(FieldValue(field2, value2)); - - EXPECT_TRUE(dim.contains(dim)); - EXPECT_TRUE(dim.contains(subDim1)); - EXPECT_TRUE(dim.contains(subDim2)); - - HashableDimensionKey subDim3; - subDim3.addValue(FieldValue(field1, value11)); - EXPECT_FALSE(dim.contains(subDim3)); - - HashableDimensionKey subDim4; - // Empty dimension is always a sub dimension of other dimensions - EXPECT_TRUE(dim.contains(subDim4)); -} - -TEST(AtomMatcherTest, TestMetric2ConditionLink) { - std::vector<int> attributionUids = {1111, 2222, 3333}; - std::vector<string> attributionTags = {"location1", "location2", "location3"}; - - LogEvent event(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event, 10 /*atomId*/, 12345, attributionUids, attributionTags, "some value"); - - FieldMatcher whatMatcher; - whatMatcher.set_field(10); - FieldMatcher* child11 = whatMatcher.add_child(); - child11->set_field(1); - child11->set_position(Position::ANY); - child11 = child11->add_child(); - child11->set_field(1); - - FieldMatcher conditionMatcher; - conditionMatcher.set_field(27); - FieldMatcher* child2 = conditionMatcher.add_child(); - child2->set_field(2); - child2->set_position(Position::LAST); - - child2 = child2->add_child(); - child2->set_field(2); - - Metric2Condition link; - - translateFieldMatcher(whatMatcher, &link.metricFields); - translateFieldMatcher(conditionMatcher, &link.conditionFields); - - ASSERT_EQ((size_t)1, link.metricFields.size()); - EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField()); - EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask); - EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag()); - - ASSERT_EQ((size_t)1, link.conditionFields.size()); - EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField()); - EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask); - EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag()); -} - -TEST(AtomMatcherTest, TestWriteDimensionPath) { - for (auto position : {Position::ANY, Position::ALL, Position::FIRST, Position::LAST}) { - FieldMatcher matcher1; - matcher1.set_field(10); - FieldMatcher* child = matcher1.add_child(); - child->set_field(2); - child->set_position(position); - child->add_child()->set_field(1); - child->add_child()->set_field(3); - - child = matcher1.add_child(); - child->set_field(4); - - child = matcher1.add_child(); - child->set_field(6); - child->add_child()->set_field(2); - - vector<Matcher> matchers; - translateFieldMatcher(matcher1, &matchers); - - android::util::ProtoOutputStream protoOut; - writeDimensionPathToProto(matchers, &protoOut); - - vector<uint8_t> outData; - outData.resize(protoOut.size()); - size_t pos = 0; - sp<ProtoReader> reader = protoOut.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - DimensionsValue result; - ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); - - EXPECT_EQ(10, result.field()); - EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case()); - ASSERT_EQ(3, result.value_tuple().dimensions_value_size()); - - const auto& dim1 = result.value_tuple().dimensions_value(0); - EXPECT_EQ(2, dim1.field()); - ASSERT_EQ(2, dim1.value_tuple().dimensions_value_size()); - - const auto& dim11 = dim1.value_tuple().dimensions_value(0); - EXPECT_EQ(1, dim11.field()); - - const auto& dim12 = dim1.value_tuple().dimensions_value(1); - EXPECT_EQ(3, dim12.field()); - - const auto& dim2 = result.value_tuple().dimensions_value(1); - EXPECT_EQ(4, dim2.field()); - - const auto& dim3 = result.value_tuple().dimensions_value(2); - EXPECT_EQ(6, dim3.field()); - ASSERT_EQ(1, dim3.value_tuple().dimensions_value_size()); - const auto& dim31 = dim3.value_tuple().dimensions_value(0); - EXPECT_EQ(2, dim31.field()); - } -} - -void checkAttributionNodeInDimensionsValueParcel(StatsDimensionsValueParcel& attributionNodeParcel, - int32_t nodeDepthInAttributionChain, - int32_t uid, string tag) { - EXPECT_EQ(attributionNodeParcel.field, nodeDepthInAttributionChain /*position at depth 1*/); - ASSERT_EQ(attributionNodeParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE); - ASSERT_EQ(attributionNodeParcel.tupleValue.size(), 2); - - StatsDimensionsValueParcel uidParcel = attributionNodeParcel.tupleValue[0]; - EXPECT_EQ(uidParcel.field, 1 /*position at depth 2*/); - EXPECT_EQ(uidParcel.valueType, STATS_DIMENSIONS_VALUE_INT_TYPE); - EXPECT_EQ(uidParcel.intValue, uid); - - StatsDimensionsValueParcel tagParcel = attributionNodeParcel.tupleValue[1]; - EXPECT_EQ(tagParcel.field, 2 /*position at depth 2*/); - EXPECT_EQ(tagParcel.valueType, STATS_DIMENSIONS_VALUE_STRING_TYPE); - EXPECT_EQ(tagParcel.stringValue, tag); -} - -// Test conversion of a HashableDimensionKey into a StatsDimensionValueParcel -TEST(AtomMatcherTest, TestSubscriberDimensionWrite) { - int atomId = 10; - // First four fields form an attribution chain - int pos1[] = {1, 1, 1}; - int pos2[] = {1, 1, 2}; - int pos3[] = {1, 2, 1}; - int pos4[] = {1, 2, 2}; - int pos5[] = {2, 1, 1}; - - Field field1(atomId, pos1, /*depth=*/2); - Field field2(atomId, pos2, /*depth=*/2); - Field field3(atomId, pos3, /*depth=*/2); - Field field4(atomId, pos4, /*depth=*/2); - Field field5(atomId, pos5, /*depth=*/0); - - Value value1((int32_t)1); - Value value2("string2"); - Value value3((int32_t)3); - Value value4("string4"); - Value value5((float)5.0); - - HashableDimensionKey dimensionKey; - dimensionKey.addValue(FieldValue(field1, value1)); - dimensionKey.addValue(FieldValue(field2, value2)); - dimensionKey.addValue(FieldValue(field3, value3)); - dimensionKey.addValue(FieldValue(field4, value4)); - dimensionKey.addValue(FieldValue(field5, value5)); - - StatsDimensionsValueParcel rootParcel = dimensionKey.toStatsDimensionsValueParcel(); - EXPECT_EQ(rootParcel.field, atomId); - ASSERT_EQ(rootParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE); - ASSERT_EQ(rootParcel.tupleValue.size(), 2); - - // Check that attribution chain is populated correctly - StatsDimensionsValueParcel attributionChainParcel = rootParcel.tupleValue[0]; - EXPECT_EQ(attributionChainParcel.field, 1 /*position at depth 0*/); - ASSERT_EQ(attributionChainParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE); - ASSERT_EQ(attributionChainParcel.tupleValue.size(), 2); - checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[0], - /*nodeDepthInAttributionChain=*/1, - value1.int_value, value2.str_value); - checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[1], - /*nodeDepthInAttributionChain=*/2, - value3.int_value, value4.str_value); - - // Check that the float is populated correctly - StatsDimensionsValueParcel floatParcel = rootParcel.tupleValue[1]; - EXPECT_EQ(floatParcel.field, 2 /*position at depth 0*/); - EXPECT_EQ(floatParcel.valueType, STATS_DIMENSIONS_VALUE_FLOAT_TYPE); - EXPECT_EQ(floatParcel.floatValue, value5.float_value); -} - -TEST(AtomMatcherTest, TestWriteDimensionToProto) { - HashableDimensionKey dim; - int pos1[] = {1, 1, 1}; - int pos2[] = {1, 1, 2}; - int pos3[] = {1, 1, 3}; - int pos4[] = {2, 0, 0}; - Field field1(10, pos1, 2); - Field field2(10, pos2, 2); - Field field3(10, pos3, 2); - Field field4(10, pos4, 0); - - Value value1((int32_t)10025); - Value value2("tag"); - Value value3((int32_t)987654); - Value value4((int32_t)99999); - - dim.addValue(FieldValue(field1, value1)); - dim.addValue(FieldValue(field2, value2)); - dim.addValue(FieldValue(field3, value3)); - dim.addValue(FieldValue(field4, value4)); - - android::util::ProtoOutputStream protoOut; - writeDimensionToProto(dim, nullptr /* include strings */, &protoOut); - - vector<uint8_t> outData; - outData.resize(protoOut.size()); - size_t pos = 0; - sp<ProtoReader> reader = protoOut.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - DimensionsValue result; - ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); - EXPECT_EQ(10, result.field()); - EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case()); - ASSERT_EQ(2, result.value_tuple().dimensions_value_size()); - - const auto& dim1 = result.value_tuple().dimensions_value(0); - EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, dim1.value_case()); - ASSERT_EQ(3, dim1.value_tuple().dimensions_value_size()); - - const auto& dim11 = dim1.value_tuple().dimensions_value(0); - EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim11.value_case()); - EXPECT_EQ(10025, dim11.value_int()); - - const auto& dim12 = dim1.value_tuple().dimensions_value(1); - EXPECT_EQ(DimensionsValue::ValueCase::kValueStr, dim12.value_case()); - EXPECT_EQ("tag", dim12.value_str()); - - const auto& dim13 = dim1.value_tuple().dimensions_value(2); - EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim13.value_case()); - EXPECT_EQ(987654, dim13.value_int()); - - const auto& dim2 = result.value_tuple().dimensions_value(1); - EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim2.value_case()); - EXPECT_EQ(99999, dim2.value_int()); -} - -TEST(AtomMatcherTest, TestWriteDimensionLeafNodesToProto) { - HashableDimensionKey dim; - int pos1[] = {1, 1, 1}; - int pos2[] = {1, 1, 2}; - int pos3[] = {1, 1, 3}; - int pos4[] = {2, 0, 0}; - Field field1(10, pos1, 2); - Field field2(10, pos2, 2); - Field field3(10, pos3, 2); - Field field4(10, pos4, 0); - - Value value1((int32_t)10025); - Value value2("tag"); - Value value3((int32_t)987654); - Value value4((int64_t)99999); - - dim.addValue(FieldValue(field1, value1)); - dim.addValue(FieldValue(field2, value2)); - dim.addValue(FieldValue(field3, value3)); - dim.addValue(FieldValue(field4, value4)); - - android::util::ProtoOutputStream protoOut; - writeDimensionLeafNodesToProto(dim, 1, nullptr /* include strings */, &protoOut); - - vector<uint8_t> outData; - outData.resize(protoOut.size()); - size_t pos = 0; - sp<ProtoReader> reader = protoOut.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - DimensionsValueTuple result; - ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); - ASSERT_EQ(4, result.dimensions_value_size()); - - const auto& dim1 = result.dimensions_value(0); - EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim1.value_case()); - EXPECT_EQ(10025, dim1.value_int()); - - const auto& dim2 = result.dimensions_value(1); - EXPECT_EQ(DimensionsValue::ValueCase::kValueStr, dim2.value_case()); - EXPECT_EQ("tag", dim2.value_str()); - - const auto& dim3 = result.dimensions_value(2); - EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim3.value_case()); - EXPECT_EQ(987654, dim3.value_int()); - - const auto& dim4 = result.dimensions_value(3); - EXPECT_EQ(DimensionsValue::ValueCase::kValueLong, dim4.value_case()); - EXPECT_EQ(99999, dim4.value_long()); -} - -TEST(AtomMatcherTest, TestWriteAtomToProto) { - std::vector<int> attributionUids = {1111, 2222}; - std::vector<string> attributionTags = {"location1", "location2"}; - - LogEvent event(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event, 4 /*atomId*/, 12345, attributionUids, attributionTags, 999); - - android::util::ProtoOutputStream protoOutput; - writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput); - - vector<uint8_t> outData; - outData.resize(protoOutput.size()); - size_t pos = 0; - sp<ProtoReader> reader = protoOutput.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - Atom result; - ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); - EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case()); - const auto& atom = result.ble_scan_result_received(); - ASSERT_EQ(2, atom.attribution_node_size()); - EXPECT_EQ(1111, atom.attribution_node(0).uid()); - EXPECT_EQ("location1", atom.attribution_node(0).tag()); - EXPECT_EQ(2222, atom.attribution_node(1).uid()); - EXPECT_EQ("location2", atom.attribution_node(1).tag()); - EXPECT_EQ(999, atom.num_results()); -} - -/* - * Test two Matchers is not a subset of one Matcher. - * Test one Matcher is subset of two Matchers. - */ -TEST(AtomMatcherTest, TestSubsetDimensions1) { - // Initialize first set of matchers - FieldMatcher matcher1; - matcher1.set_field(10); - - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - child->set_position(Position::ALL); - child->add_child()->set_field(1); - child->add_child()->set_field(2); - - vector<Matcher> matchers1; - translateFieldMatcher(matcher1, &matchers1); - ASSERT_EQ(2, matchers1.size()); - - // Initialize second set of matchers - FieldMatcher matcher2; - matcher2.set_field(10); - - child = matcher2.add_child(); - child->set_field(1); - child->set_position(Position::ALL); - child->add_child()->set_field(1); - - vector<Matcher> matchers2; - translateFieldMatcher(matcher2, &matchers2); - ASSERT_EQ(1, matchers2.size()); - - EXPECT_FALSE(subsetDimensions(matchers1, matchers2)); - EXPECT_TRUE(subsetDimensions(matchers2, matchers1)); -} -/* - * Test not a subset with one matching Matcher, one non-matching Matcher. - */ -TEST(AtomMatcherTest, TestSubsetDimensions2) { - // Initialize first set of matchers - FieldMatcher matcher1; - matcher1.set_field(10); - - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - - child = matcher1.add_child(); - child->set_field(2); - - vector<Matcher> matchers1; - translateFieldMatcher(matcher1, &matchers1); - - // Initialize second set of matchers - FieldMatcher matcher2; - matcher2.set_field(10); - - child = matcher2.add_child(); - child->set_field(1); - - child = matcher2.add_child(); - child->set_field(3); - - vector<Matcher> matchers2; - translateFieldMatcher(matcher2, &matchers2); - - EXPECT_FALSE(subsetDimensions(matchers1, matchers2)); -} - -/* - * Test not a subset if parent field is not equal. - */ -TEST(AtomMatcherTest, TestSubsetDimensions3) { - // Initialize first set of matchers - FieldMatcher matcher1; - matcher1.set_field(10); - - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - - vector<Matcher> matchers1; - translateFieldMatcher(matcher1, &matchers1); - - // Initialize second set of matchers - FieldMatcher matcher2; - matcher2.set_field(5); - - child = matcher2.add_child(); - child->set_field(1); - - vector<Matcher> matchers2; - translateFieldMatcher(matcher2, &matchers2); - - EXPECT_FALSE(subsetDimensions(matchers1, matchers2)); -} - -/* - * Test is subset with two matching Matchers. - */ -TEST(AtomMatcherTest, TestSubsetDimensions4) { - // Initialize first set of matchers - FieldMatcher matcher1; - matcher1.set_field(10); - - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - - child = matcher1.add_child(); - child->set_field(2); - - vector<Matcher> matchers1; - translateFieldMatcher(matcher1, &matchers1); - - // Initialize second set of matchers - FieldMatcher matcher2; - matcher2.set_field(10); - - child = matcher2.add_child(); - child->set_field(1); - - child = matcher2.add_child(); - child->set_field(2); - - child = matcher2.add_child(); - child->set_field(3); - - vector<Matcher> matchers2; - translateFieldMatcher(matcher2, &matchers2); - - EXPECT_TRUE(subsetDimensions(matchers1, matchers2)); - EXPECT_FALSE(subsetDimensions(matchers2, matchers1)); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/HashableDimensionKey_test.cpp b/cmds/statsd/tests/HashableDimensionKey_test.cpp deleted file mode 100644 index 29adcd08a7b8..000000000000 --- a/cmds/statsd/tests/HashableDimensionKey_test.cpp +++ /dev/null @@ -1,137 +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. - */ -#include "src/HashableDimensionKey.h" - -#include <gtest/gtest.h> - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "statsd_test_util.h" - -#ifdef __ANDROID__ - -using android::util::ProtoReader; - -namespace android { -namespace os { -namespace statsd { - -/** - * Test that #containsLinkedStateValues returns false when the whatKey is - * smaller than the primaryKey. - */ -TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_WhatKeyTooSmall) { - std::vector<Metric2State> mMetric2StateLinks; - - int32_t uid1 = 1000; - HashableDimensionKey whatKey = DEFAULT_DIMENSION_KEY; - HashableDimensionKey primaryKey; - getUidProcessKey(uid1, &primaryKey); - - EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, - UID_PROCESS_STATE_ATOM_ID)); -} - -/** - * Test that #containsLinkedStateValues returns false when the linked values - * are not equal. - */ -TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_UnequalLinkedValues) { - int stateAtomId = UID_PROCESS_STATE_ATOM_ID; - - FieldMatcher whatMatcher; - whatMatcher.set_field(util::OVERLAY_STATE_CHANGED); - FieldMatcher* child11 = whatMatcher.add_child(); - child11->set_field(1); - - FieldMatcher stateMatcher; - stateMatcher.set_field(stateAtomId); - FieldMatcher* child21 = stateMatcher.add_child(); - child21->set_field(1); - - std::vector<Metric2State> mMetric2StateLinks; - Metric2State ms; - ms.stateAtomId = stateAtomId; - translateFieldMatcher(whatMatcher, &ms.metricFields); - translateFieldMatcher(stateMatcher, &ms.stateFields); - mMetric2StateLinks.push_back(ms); - - int32_t uid1 = 1000; - int32_t uid2 = 1001; - HashableDimensionKey whatKey; - getOverlayKey(uid2, "package", &whatKey); - HashableDimensionKey primaryKey; - getUidProcessKey(uid1, &primaryKey); - - EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId)); -} - -/** - * Test that #containsLinkedStateValues returns false when there is no link - * between the key values. - */ -TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_MissingMetric2StateLinks) { - int stateAtomId = UID_PROCESS_STATE_ATOM_ID; - - std::vector<Metric2State> mMetric2StateLinks; - - int32_t uid1 = 1000; - HashableDimensionKey whatKey; - getOverlayKey(uid1, "package", &whatKey); - HashableDimensionKey primaryKey; - getUidProcessKey(uid1, &primaryKey); - - EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId)); -} - -/** - * Test that #containsLinkedStateValues returns true when the key values are - * linked and equal. - */ -TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_AllConditionsMet) { - int stateAtomId = UID_PROCESS_STATE_ATOM_ID; - - FieldMatcher whatMatcher; - whatMatcher.set_field(util::OVERLAY_STATE_CHANGED); - FieldMatcher* child11 = whatMatcher.add_child(); - child11->set_field(1); - - FieldMatcher stateMatcher; - stateMatcher.set_field(stateAtomId); - FieldMatcher* child21 = stateMatcher.add_child(); - child21->set_field(1); - - std::vector<Metric2State> mMetric2StateLinks; - Metric2State ms; - ms.stateAtomId = stateAtomId; - translateFieldMatcher(whatMatcher, &ms.metricFields); - translateFieldMatcher(stateMatcher, &ms.stateFields); - mMetric2StateLinks.push_back(ms); - - int32_t uid1 = 1000; - HashableDimensionKey whatKey; - getOverlayKey(uid1, "package", &whatKey); - HashableDimensionKey primaryKey; - getUidProcessKey(uid1, &primaryKey); - - EXPECT_TRUE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId)); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp deleted file mode 100644 index 92cd04f37ee0..000000000000 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ /dev/null @@ -1,809 +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. - -#include <gtest/gtest.h> -#include <stdio.h> - -#include "annotations.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/matcher_util.h" -#include "stats_event.h" -#include "stats_log_util.h" -#include "stats_util.h" -#include "statsd_test_util.h" - -using namespace android::os::statsd; -using std::unordered_map; -using std::vector; - -const int32_t TAG_ID = 123; -const int32_t TAG_ID_2 = 28; // hardcoded tag of atom with uid field -const int FIELD_ID_1 = 1; -const int FIELD_ID_2 = 2; -const int FIELD_ID_3 = 2; - -const int ATTRIBUTION_UID_FIELD_ID = 1; -const int ATTRIBUTION_TAG_FIELD_ID = 2; - - -#ifdef __ANDROID__ - -namespace { - -void makeIntLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const int32_t value) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - AStatsEvent_writeInt32(statsEvent, value); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const float floatValue) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - AStatsEvent_writeFloat(statsEvent, floatValue); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const string& name) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - AStatsEvent_writeString(statsEvent, name.c_str()); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeIntWithBoolAnnotationLogEvent(LogEvent* logEvent, const int32_t atomId, - const int32_t field, const uint8_t annotationId, - const bool annotationValue) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_writeInt32(statsEvent, field); - AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const vector<int>& attributionUids, - const vector<string>& attributionTags, const string& name) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, name.c_str()); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const bool bool1, const bool bool2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - AStatsEvent_writeBool(statsEvent, bool1); - AStatsEvent_writeBool(statsEvent, bool2); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -} // anonymous namespace - -TEST(AtomMatcherTest, TestSimpleMatcher) { - sp<UidMap> uidMap = new UidMap(); - - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - - LogEvent event(/*uid=*/0, /*pid=*/0); - makeIntLogEvent(&event, TAG_ID, 0, 11); - - // Test - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - // Wrong tag id. - simpleMatcher->set_atom_id(TAG_ID + 1); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestAttributionMatcher) { - sp<UidMap> uidMap = new UidMap(); - std::vector<int> attributionUids = {1111, 2222, 3333}; - std::vector<string> attributionTags = {"location1", "location2", "location3"}; - - // Set up the log event. - LogEvent event(/*uid=*/0, /*pid=*/0); - makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value"); - - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - - // Match first node. - auto attributionMatcher = simpleMatcher->add_field_value_matcher(); - attributionMatcher->set_field(FIELD_ID_1); - attributionMatcher->set_position(Position::FIRST); - attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( - ATTRIBUTION_TAG_FIELD_ID); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "tag"); - - auto fieldMatcher = simpleMatcher->add_field_value_matcher(); - fieldMatcher->set_field(FIELD_ID_2); - fieldMatcher->set_eq_string("some value"); - - // Tag not matched. - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location3"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - // Match last node. - attributionMatcher->set_position(Position::LAST); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - // Match any node. - attributionMatcher->set_position(Position::ANY); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location2"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location4"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - // Attribution match but primitive field not match. - attributionMatcher->set_position(Position::ANY); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location2"); - fieldMatcher->set_eq_string("wrong value"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - fieldMatcher->set_eq_string("some value"); - - // Uid match. - attributionMatcher->set_position(Position::ANY); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field( - ATTRIBUTION_UID_FIELD_ID); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - uidMap->updateMap( - 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, - {android::String16("v1"), android::String16("v1"), android::String16("v2"), - android::String16("v1"), android::String16("v2")}, - {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), - android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, - {android::String16(""), android::String16(""), android::String16(""), - android::String16(""), android::String16("")}); - - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::FIRST); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::LAST); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - // Uid + tag. - attributionMatcher->set_position(Position::ANY); - attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( - ATTRIBUTION_TAG_FIELD_ID); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location2"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::FIRST); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location2"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::LAST); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location2"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestUidFieldMatcher) { - sp<UidMap> uidMap = new UidMap(); - uidMap->updateMap( - 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, - {android::String16("v1"), android::String16("v1"), android::String16("v2"), - android::String16("v1"), android::String16("v2")}, - {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), - android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, - {android::String16(""), android::String16(""), android::String16(""), - android::String16(""), android::String16("")}); - - // Set up matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - simpleMatcher->add_field_value_matcher()->set_field(1); - simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0"); - - // Make event without is_uid annotation. - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeIntLogEvent(&event1, TAG_ID, 0, 1111); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1)); - - // Make event with is_uid annotation. - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID_2, 1111, ANNOTATION_ID_IS_UID, true); - - // Event has is_uid annotation, so mapping from uid to package name occurs. - simpleMatcher->set_atom_id(TAG_ID_2); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); - - // Event has is_uid annotation, but uid maps to different package name. - simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2)); -} - -TEST(AtomMatcherTest, TestNeqAnyStringMatcher) { - sp<UidMap> uidMap = new UidMap(); - uidMap->updateMap( - 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, - {android::String16("v1"), android::String16("v1"), android::String16("v2"), - android::String16("v1"), android::String16("v2")}, - {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), - android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, - {android::String16(""), android::String16(""), android::String16(""), - android::String16(""), android::String16("")}); - - std::vector<int> attributionUids = {1111, 2222, 3333, 1066}; - std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"}; - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value"); - - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - - // Match first node. - auto attributionMatcher = simpleMatcher->add_field_value_matcher(); - attributionMatcher->set_field(FIELD_ID_1); - attributionMatcher->set_position(Position::FIRST); - attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( - ATTRIBUTION_UID_FIELD_ID); - auto neqStringList = attributionMatcher->mutable_matches_tuple() - ->mutable_field_value_matcher(0) - ->mutable_neq_any_string(); - neqStringList->add_str_value("pkg2"); - neqStringList->add_str_value("pkg3"); - - auto fieldMatcher = simpleMatcher->add_field_value_matcher(); - fieldMatcher->set_field(FIELD_ID_2); - fieldMatcher->set_eq_string("some value"); - - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - neqStringList->Clear(); - neqStringList->add_str_value("pkg1"); - neqStringList->add_str_value("pkg3"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::ANY); - neqStringList->Clear(); - neqStringList->add_str_value("maps.com"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - neqStringList->Clear(); - neqStringList->add_str_value("PkG3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::LAST); - neqStringList->Clear(); - neqStringList->add_str_value("AID_STATSD"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestEqAnyStringMatcher) { - sp<UidMap> uidMap = new UidMap(); - uidMap->updateMap( - 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, - {android::String16("v1"), android::String16("v1"), android::String16("v2"), - android::String16("v1"), android::String16("v2")}, - {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), - android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, - {android::String16(""), android::String16(""), android::String16(""), - android::String16(""), android::String16("")}); - - std::vector<int> attributionUids = {1067, 2222, 3333, 1066}; - std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"}; - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value"); - - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - - // Match first node. - auto attributionMatcher = simpleMatcher->add_field_value_matcher(); - attributionMatcher->set_field(FIELD_ID_1); - attributionMatcher->set_position(Position::FIRST); - attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( - ATTRIBUTION_UID_FIELD_ID); - auto eqStringList = attributionMatcher->mutable_matches_tuple() - ->mutable_field_value_matcher(0) - ->mutable_eq_any_string(); - eqStringList->add_str_value("AID_ROOT"); - eqStringList->add_str_value("AID_INCIDENTD"); - - auto fieldMatcher = simpleMatcher->add_field_value_matcher(); - fieldMatcher->set_field(FIELD_ID_2); - fieldMatcher->set_eq_string("some value"); - - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::ANY); - eqStringList->Clear(); - eqStringList->add_str_value("AID_STATSD"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - eqStringList->Clear(); - eqStringList->add_str_value("pkg1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - auto normalStringField = fieldMatcher->mutable_eq_any_string(); - normalStringField->add_str_value("some value123"); - normalStringField->add_str_value("some value"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - normalStringField->Clear(); - normalStringField->add_str_value("AID_STATSD"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - eqStringList->Clear(); - eqStringList->add_str_value("maps.com"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestBoolMatcher) { - sp<UidMap> uidMap = new UidMap(); - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - auto keyValue1 = simpleMatcher->add_field_value_matcher(); - keyValue1->set_field(FIELD_ID_1); - auto keyValue2 = simpleMatcher->add_field_value_matcher(); - keyValue2->set_field(FIELD_ID_2); - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - makeBoolLogEvent(&event, TAG_ID, 0, true, false); - - // Test - keyValue1->set_eq_bool(true); - keyValue2->set_eq_bool(false); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - keyValue1->set_eq_bool(false); - keyValue2->set_eq_bool(false); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - keyValue1->set_eq_bool(false); - keyValue2->set_eq_bool(true); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - keyValue1->set_eq_bool(true); - keyValue2->set_eq_bool(true); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestStringMatcher) { - sp<UidMap> uidMap = new UidMap(); - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - auto keyValue = simpleMatcher->add_field_value_matcher(); - keyValue->set_field(FIELD_ID_1); - keyValue->set_eq_string("some value"); - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - makeStringLogEvent(&event, TAG_ID, 0, "some value"); - - // Test - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestMultiFieldsMatcher) { - sp<UidMap> uidMap = new UidMap(); - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - auto keyValue1 = simpleMatcher->add_field_value_matcher(); - keyValue1->set_field(FIELD_ID_1); - auto keyValue2 = simpleMatcher->add_field_value_matcher(); - keyValue2->set_field(FIELD_ID_2); - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event, TAG_ID, 0, 2, 3); - - // Test - keyValue1->set_eq_int(2); - keyValue2->set_eq_int(3); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - keyValue1->set_eq_int(2); - keyValue2->set_eq_int(4); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - keyValue1->set_eq_int(4); - keyValue2->set_eq_int(3); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestIntComparisonMatcher) { - sp<UidMap> uidMap = new UidMap(); - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - - simpleMatcher->set_atom_id(TAG_ID); - auto keyValue = simpleMatcher->add_field_value_matcher(); - keyValue->set_field(FIELD_ID_1); - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - makeIntLogEvent(&event, TAG_ID, 0, 11); - - // Test - - // eq_int - keyValue->set_eq_int(10); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_eq_int(11); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_eq_int(12); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - // lt_int - keyValue->set_lt_int(10); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_lt_int(11); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_lt_int(12); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - // lte_int - keyValue->set_lte_int(10); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_lte_int(11); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_lte_int(12); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - // gt_int - keyValue->set_gt_int(10); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_gt_int(11); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_gt_int(12); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - // gte_int - keyValue->set_gte_int(10); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_gte_int(11); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_gte_int(12); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestFloatComparisonMatcher) { - sp<UidMap> uidMap = new UidMap(); - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - - auto keyValue = simpleMatcher->add_field_value_matcher(); - keyValue->set_field(FIELD_ID_1); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeFloatLogEvent(&event1, TAG_ID, 0, 10.1f); - keyValue->set_lt_float(10.0); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1)); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeFloatLogEvent(&event2, TAG_ID, 0, 9.9f); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); - - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeFloatLogEvent(&event3, TAG_ID, 0, 10.1f); - keyValue->set_gt_float(10.0); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3)); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - makeFloatLogEvent(&event4, TAG_ID, 0, 9.9f); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4)); -} - -// Helper for the composite matchers. -void addSimpleMatcher(SimpleAtomMatcher* simpleMatcher, int tag, int key, int val) { - simpleMatcher->set_atom_id(tag); - auto keyValue = simpleMatcher->add_field_value_matcher(); - keyValue->set_field(key); - keyValue->set_eq_int(val); -} - -TEST(AtomMatcherTest, TestAndMatcher) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::AND; - - vector<int> children; - children.push_back(0); - children.push_back(1); - children.push_back(2); - - vector<MatchingState> matcherResults; - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kMatched); - - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kMatched); - - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); -} - -TEST(AtomMatcherTest, TestOrMatcher) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::OR; - - vector<int> children; - children.push_back(0); - children.push_back(1); - children.push_back(2); - - vector<MatchingState> matcherResults; - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kMatched); - - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kNotMatched); - - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); -} - -TEST(AtomMatcherTest, TestNotMatcher) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NOT; - - vector<int> children; - children.push_back(0); - - vector<MatchingState> matcherResults; - matcherResults.push_back(MatchingState::kMatched); - - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kNotMatched); - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); -} - -TEST(AtomMatcherTest, TestNandMatcher) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NAND; - - vector<int> children; - children.push_back(0); - children.push_back(1); - - vector<MatchingState> matcherResults; - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kNotMatched); - - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kNotMatched); - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kMatched); - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); -} - -TEST(AtomMatcherTest, TestNorMatcher) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NOR; - - vector<int> children; - children.push_back(0); - children.push_back(1); - - vector<MatchingState> matcherResults; - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kNotMatched); - - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kNotMatched); - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kMatched); - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); -} -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp deleted file mode 100644 index bde59f497b19..000000000000 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ /dev/null @@ -1,371 +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. - -#include "src/logd/LogEvent.h" - -#include <gtest/gtest.h> - -#include "frameworks/proto_logging/stats/atoms.pb.h" -#include "frameworks/proto_logging/stats/enums/stats/launcher/launcher.pb.h" -#include "log/log_event_list.h" -#include "stats_event.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -using std::string; -using std::vector; -using util::ProtoOutputStream; -using util::ProtoReader; - -namespace { - -Field getField(int32_t tag, const vector<int32_t>& pos, int32_t depth, const vector<bool>& last) { - Field f(tag, (int32_t*)pos.data(), depth); - - // For loop starts at 1 because the last field at depth 0 is not decorated. - for (int i = 1; i < depth; i++) { - if (last[i]) f.decorateLastPos(i); - } - - return f; -} - -void createIntWithBoolAnnotationLogEvent(LogEvent* logEvent, uint8_t annotationId, - bool annotationValue) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, /*atomId=*/100); - AStatsEvent_writeInt32(statsEvent, 10); - AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - EXPECT_TRUE(logEvent->parseBuffer(buf, size)); - - AStatsEvent_release(statsEvent); -} - -void createIntWithIntAnnotationLogEvent(LogEvent* logEvent, uint8_t annotationId, - int annotationValue) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, /*atomId=*/100); - AStatsEvent_writeInt32(statsEvent, 10); - AStatsEvent_addInt32Annotation(statsEvent, annotationId, annotationValue); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - EXPECT_TRUE(logEvent->parseBuffer(buf, size)); - - AStatsEvent_release(statsEvent); -} - -} // anonymous namespace - -TEST(LogEventTest, TestPrimitiveParsing) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - AStatsEvent_writeInt32(event, 10); - AStatsEvent_writeInt64(event, 0x123456789); - AStatsEvent_writeFloat(event, 2.0); - AStatsEvent_writeBool(event, true); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - - LogEvent logEvent(/*uid=*/1000, /*pid=*/1001); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - - EXPECT_EQ(100, logEvent.GetTagId()); - EXPECT_EQ(1000, logEvent.GetUid()); - EXPECT_EQ(1001, logEvent.GetPid()); - EXPECT_FALSE(logEvent.hasAttributionChain()); - - const vector<FieldValue>& values = logEvent.getValues(); - ASSERT_EQ(4, values.size()); - - const FieldValue& int32Item = values[0]; - Field expectedField = getField(100, {1, 1, 1}, 0, {false, false, false}); - EXPECT_EQ(expectedField, int32Item.mField); - EXPECT_EQ(Type::INT, int32Item.mValue.getType()); - EXPECT_EQ(10, int32Item.mValue.int_value); - - const FieldValue& int64Item = values[1]; - expectedField = getField(100, {2, 1, 1}, 0, {false, false, false}); - EXPECT_EQ(expectedField, int64Item.mField); - EXPECT_EQ(Type::LONG, int64Item.mValue.getType()); - EXPECT_EQ(0x123456789, int64Item.mValue.long_value); - - const FieldValue& floatItem = values[2]; - expectedField = getField(100, {3, 1, 1}, 0, {false, false, false}); - EXPECT_EQ(expectedField, floatItem.mField); - EXPECT_EQ(Type::FLOAT, floatItem.mValue.getType()); - EXPECT_EQ(2.0, floatItem.mValue.float_value); - - const FieldValue& boolItem = values[3]; - expectedField = getField(100, {4, 1, 1}, 0, {true, false, false}); - EXPECT_EQ(expectedField, boolItem.mField); - EXPECT_EQ(Type::INT, boolItem.mValue.getType()); // FieldValue does not support boolean type - EXPECT_EQ(1, boolItem.mValue.int_value); - - AStatsEvent_release(event); -} - -TEST(LogEventTest, TestStringAndByteArrayParsing) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - string str = "test"; - AStatsEvent_writeString(event, str.c_str()); - AStatsEvent_writeByteArray(event, (uint8_t*)str.c_str(), str.length()); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - - LogEvent logEvent(/*uid=*/1000, /*pid=*/1001); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - - EXPECT_EQ(100, logEvent.GetTagId()); - EXPECT_EQ(1000, logEvent.GetUid()); - EXPECT_EQ(1001, logEvent.GetPid()); - EXPECT_FALSE(logEvent.hasAttributionChain()); - - const vector<FieldValue>& values = logEvent.getValues(); - ASSERT_EQ(2, values.size()); - - const FieldValue& stringItem = values[0]; - Field expectedField = getField(100, {1, 1, 1}, 0, {false, false, false}); - EXPECT_EQ(expectedField, stringItem.mField); - EXPECT_EQ(Type::STRING, stringItem.mValue.getType()); - EXPECT_EQ(str, stringItem.mValue.str_value); - - const FieldValue& storageItem = values[1]; - expectedField = getField(100, {2, 1, 1}, 0, {true, false, false}); - EXPECT_EQ(expectedField, storageItem.mField); - EXPECT_EQ(Type::STORAGE, storageItem.mValue.getType()); - vector<uint8_t> expectedValue = {'t', 'e', 's', 't'}; - EXPECT_EQ(expectedValue, storageItem.mValue.storage_value); - - AStatsEvent_release(event); -} - -TEST(LogEventTest, TestEmptyString) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - string empty = ""; - AStatsEvent_writeString(event, empty.c_str()); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - - LogEvent logEvent(/*uid=*/1000, /*pid=*/1001); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - - EXPECT_EQ(100, logEvent.GetTagId()); - EXPECT_EQ(1000, logEvent.GetUid()); - EXPECT_EQ(1001, logEvent.GetPid()); - EXPECT_FALSE(logEvent.hasAttributionChain()); - - const vector<FieldValue>& values = logEvent.getValues(); - ASSERT_EQ(1, values.size()); - - const FieldValue& item = values[0]; - Field expectedField = getField(100, {1, 1, 1}, 0, {true, false, false}); - EXPECT_EQ(expectedField, item.mField); - EXPECT_EQ(Type::STRING, item.mValue.getType()); - EXPECT_EQ(empty, item.mValue.str_value); - - AStatsEvent_release(event); -} - -TEST(LogEventTest, TestByteArrayWithNullCharacter) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - uint8_t message[] = {'\t', 'e', '\0', 's', 't'}; - AStatsEvent_writeByteArray(event, message, 5); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - - LogEvent logEvent(/*uid=*/1000, /*pid=*/1001); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - - EXPECT_EQ(100, logEvent.GetTagId()); - EXPECT_EQ(1000, logEvent.GetUid()); - EXPECT_EQ(1001, logEvent.GetPid()); - - const vector<FieldValue>& values = logEvent.getValues(); - ASSERT_EQ(1, values.size()); - - const FieldValue& item = values[0]; - Field expectedField = getField(100, {1, 1, 1}, 0, {true, false, false}); - EXPECT_EQ(expectedField, item.mField); - EXPECT_EQ(Type::STORAGE, item.mValue.getType()); - vector<uint8_t> expectedValue(message, message + 5); - EXPECT_EQ(expectedValue, item.mValue.storage_value); - - AStatsEvent_release(event); -} - -TEST(LogEventTest, TestAttributionChain) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - - string tag1 = "tag1"; - string tag2 = "tag2"; - - uint32_t uids[] = {1001, 1002}; - const char* tags[] = {tag1.c_str(), tag2.c_str()}; - - AStatsEvent_writeAttributionChain(event, uids, tags, 2); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - - LogEvent logEvent(/*uid=*/1000, /*pid=*/1001); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - - EXPECT_EQ(100, logEvent.GetTagId()); - EXPECT_EQ(1000, logEvent.GetUid()); - EXPECT_EQ(1001, logEvent.GetPid()); - - const vector<FieldValue>& values = logEvent.getValues(); - ASSERT_EQ(4, values.size()); // 2 per attribution node - - std::pair<int, int> attrIndexRange; - EXPECT_TRUE(logEvent.hasAttributionChain(&attrIndexRange)); - EXPECT_EQ(0, attrIndexRange.first); - EXPECT_EQ(3, attrIndexRange.second); - - // Check first attribution node - const FieldValue& uid1Item = values[0]; - Field expectedField = getField(100, {1, 1, 1}, 2, {true, false, false}); - EXPECT_EQ(expectedField, uid1Item.mField); - EXPECT_EQ(Type::INT, uid1Item.mValue.getType()); - EXPECT_EQ(1001, uid1Item.mValue.int_value); - - const FieldValue& tag1Item = values[1]; - expectedField = getField(100, {1, 1, 2}, 2, {true, false, true}); - EXPECT_EQ(expectedField, tag1Item.mField); - EXPECT_EQ(Type::STRING, tag1Item.mValue.getType()); - EXPECT_EQ(tag1, tag1Item.mValue.str_value); - - // Check second attribution nodes - const FieldValue& uid2Item = values[2]; - expectedField = getField(100, {1, 2, 1}, 2, {true, true, false}); - EXPECT_EQ(expectedField, uid2Item.mField); - EXPECT_EQ(Type::INT, uid2Item.mValue.getType()); - EXPECT_EQ(1002, uid2Item.mValue.int_value); - - const FieldValue& tag2Item = values[3]; - expectedField = getField(100, {1, 2, 2}, 2, {true, true, true}); - EXPECT_EQ(expectedField, tag2Item.mField); - EXPECT_EQ(Type::STRING, tag2Item.mValue.getType()); - EXPECT_EQ(tag2, tag2Item.mValue.str_value); - - AStatsEvent_release(event); -} - -TEST(LogEventTest, TestAnnotationIdIsUid) { - LogEvent event(/*uid=*/0, /*pid=*/0); - createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_IS_UID, true); - - const vector<FieldValue>& values = event.getValues(); - ASSERT_EQ(values.size(), 1); - EXPECT_EQ(event.getUidFieldIndex(), 0); -} - -TEST(LogEventTest, TestAnnotationIdStateNested) { - LogEvent event(/*uid=*/0, /*pid=*/0); - createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_STATE_NESTED, true); - - const vector<FieldValue>& values = event.getValues(); - ASSERT_EQ(values.size(), 1); - EXPECT_TRUE(values[0].mAnnotations.isNested()); -} - -TEST(LogEventTest, TestPrimaryFieldAnnotation) { - LogEvent event(/*uid=*/0, /*pid=*/0); - createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_PRIMARY_FIELD, true); - - const vector<FieldValue>& values = event.getValues(); - ASSERT_EQ(values.size(), 1); - EXPECT_TRUE(values[0].mAnnotations.isPrimaryField()); -} - -TEST(LogEventTest, TestExclusiveStateAnnotation) { - LogEvent event(/*uid=*/0, /*pid=*/0); - createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_EXCLUSIVE_STATE, true); - - const vector<FieldValue>& values = event.getValues(); - ASSERT_EQ(values.size(), 1); - EXPECT_TRUE(values[0].mAnnotations.isExclusiveState()); -} - -TEST(LogEventTest, TestPrimaryFieldFirstUidAnnotation) { - // Event has 10 ints and then an attribution chain - int numInts = 10; - int firstUidInChainIndex = numInts; - string tag1 = "tag1"; - string tag2 = "tag2"; - uint32_t uids[] = {1001, 1002}; - const char* tags[] = {tag1.c_str(), tag2.c_str()}; - - // Construct AStatsEvent - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 100); - for (int i = 0; i < numInts; i++) { - AStatsEvent_writeInt32(statsEvent, 10); - } - AStatsEvent_writeAttributionChain(statsEvent, uids, tags, 2); - AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true); - AStatsEvent_build(statsEvent); - - // Construct LogEvent - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - LogEvent logEvent(/*uid=*/0, /*pid=*/0); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - AStatsEvent_release(statsEvent); - - // Check annotation - const vector<FieldValue>& values = logEvent.getValues(); - ASSERT_EQ(values.size(), numInts + 4); - EXPECT_TRUE(values[firstUidInChainIndex].mAnnotations.isPrimaryField()); -} - -TEST(LogEventTest, TestResetStateAnnotation) { - int32_t resetState = 10; - LogEvent event(/*uid=*/0, /*pid=*/0); - createIntWithIntAnnotationLogEvent(&event, ANNOTATION_ID_TRIGGER_STATE_RESET, resetState); - - const vector<FieldValue>& values = event.getValues(); - ASSERT_EQ(values.size(), 1); - EXPECT_EQ(event.getResetState(), resetState); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/LogReader_test.cpp b/cmds/statsd/tests/LogReader_test.cpp deleted file mode 100644 index 7ce1d6a71c85..000000000000 --- a/cmds/statsd/tests/LogReader_test.cpp +++ /dev/null @@ -1,21 +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. - -#include <gtest/gtest.h> - -#include <stdio.h> - -TEST(LogReaderTest, TestNothingAtAll) { - printf("yay!"); -} diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp deleted file mode 100644 index f05ec490dcee..000000000000 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ /dev/null @@ -1,325 +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. - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <private/android_filesystem_config.h> -#include <stdio.h> - -#include <set> -#include <unordered_map> -#include <vector> - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "metrics/metrics_test_helper.h" -#include "src/condition/ConditionTracker.h" -#include "src/matchers/AtomMatchingTracker.h" -#include "src/metrics/CountMetricProducer.h" -#include "src/metrics/GaugeMetricProducer.h" -#include "src/metrics/MetricProducer.h" -#include "src/metrics/ValueMetricProducer.h" -#include "src/metrics/parsing_utils/metrics_manager_util.h" -#include "src/state/StateManager.h" -#include "statsd_test_util.h" - -using namespace testing; -using android::sp; -using android::os::statsd::Predicate; -using std::map; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -namespace { -const ConfigKey kConfigKey(0, 12345); - -const long timeBaseSec = 1000; - -StatsdConfig buildGoodConfig() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_OFF")); - - simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF")); - - AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(StringToId("SCREEN_IS_ON")); - combination->add_matcher(StringToId("SCREEN_IS_OFF")); - - CountMetric* metric = config.add_count_metric(); - metric->set_id(3); - metric->set_what(StringToId("SCREEN_IS_ON")); - metric->set_bucket(ONE_MINUTE); - metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/); - metric->mutable_dimensions_in_what()->add_child()->set_field(1); - return config; -} - -set<int32_t> unionSet(const vector<set<int32_t>> sets) { - set<int32_t> toRet; - for (const set<int32_t>& s : sets) { - toRet.insert(s.begin(), s.end()); - } - return toRet; -} -} // anonymous namespace - -TEST(MetricsManagerTest, TestLogSources) { - string app1 = "app1"; - set<int32_t> app1Uids = {1111, 11111}; - string app2 = "app2"; - set<int32_t> app2Uids = {2222}; - string app3 = "app3"; - set<int32_t> app3Uids = {3333, 1111}; - - map<string, set<int32_t>> pkgToUids; - pkgToUids[app1] = app1Uids; - pkgToUids[app2] = app2Uids; - pkgToUids[app3] = app3Uids; - - int32_t atom1 = 10, atom2 = 20, atom3 = 30; - sp<MockUidMap> uidMap = new StrictMock<MockUidMap>(); - EXPECT_CALL(*uidMap, getAppUid(_)) - .Times(4) - .WillRepeatedly(Invoke([&pkgToUids](const string& pkg) { - const auto& it = pkgToUids.find(pkg); - if (it != pkgToUids.end()) { - return it->second; - } - return set<int32_t>(); - })); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterPullUidProvider(kConfigKey, _)).Times(1); - EXPECT_CALL(*pullerManager, UnregisterPullUidProvider(kConfigKey, _)).Times(1); - - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - - StatsdConfig config; - config.add_allowed_log_source("AID_SYSTEM"); - config.add_allowed_log_source(app1); - config.add_default_pull_packages("AID_SYSTEM"); - config.add_default_pull_packages("AID_ROOT"); - - const set<int32_t> defaultPullUids = {AID_SYSTEM, AID_ROOT}; - - PullAtomPackages* pullAtomPackages = config.add_pull_atom_packages(); - pullAtomPackages->set_atom_id(atom1); - pullAtomPackages->add_packages(app1); - pullAtomPackages->add_packages(app3); - - pullAtomPackages = config.add_pull_atom_packages(); - pullAtomPackages->set_atom_id(atom2); - pullAtomPackages->add_packages(app2); - pullAtomPackages->add_packages("AID_STATSD"); - - MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap, - pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor); - EXPECT_TRUE(metricsManager.isConfigValid()); - - EXPECT_THAT(metricsManager.mAllowedUid, ElementsAre(AID_SYSTEM)); - EXPECT_THAT(metricsManager.mAllowedPkg, ElementsAre(app1)); - EXPECT_THAT(metricsManager.mAllowedLogSources, - ContainerEq(unionSet(vector<set<int32_t>>({app1Uids, {AID_SYSTEM}})))); - EXPECT_THAT(metricsManager.mDefaultPullUids, ContainerEq(defaultPullUids)); - - vector<int32_t> atom1Uids = metricsManager.getPullAtomUids(atom1); - EXPECT_THAT(atom1Uids, - UnorderedElementsAreArray(unionSet({defaultPullUids, app1Uids, app3Uids}))); - - vector<int32_t> atom2Uids = metricsManager.getPullAtomUids(atom2); - EXPECT_THAT(atom2Uids, - UnorderedElementsAreArray(unionSet({defaultPullUids, app2Uids, {AID_STATSD}}))); - - vector<int32_t> atom3Uids = metricsManager.getPullAtomUids(atom3); - EXPECT_THAT(atom3Uids, UnorderedElementsAreArray(defaultPullUids)); -} - -TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate) { - string app1 = "app1"; - set<int32_t> app1Uids = {1111, 11111}; - string app2 = "app2"; - set<int32_t> app2Uids = {2222}; - string app3 = "app3"; - set<int32_t> app3Uids = {3333, 1111}; - - map<string, set<int32_t>> pkgToUids; - pkgToUids[app1] = app1Uids; - pkgToUids[app2] = app2Uids; - pkgToUids[app3] = app3Uids; - - int32_t atom1 = 10, atom2 = 20, atom3 = 30; - sp<MockUidMap> uidMap = new StrictMock<MockUidMap>(); - EXPECT_CALL(*uidMap, getAppUid(_)) - .Times(8) - .WillRepeatedly(Invoke([&pkgToUids](const string& pkg) { - const auto& it = pkgToUids.find(pkg); - if (it != pkgToUids.end()) { - return it->second; - } - return set<int32_t>(); - })); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterPullUidProvider(kConfigKey, _)).Times(1); - EXPECT_CALL(*pullerManager, UnregisterPullUidProvider(kConfigKey, _)).Times(1); - - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - - StatsdConfig config; - config.add_allowed_log_source("AID_SYSTEM"); - config.add_allowed_log_source(app1); - config.add_default_pull_packages("AID_SYSTEM"); - config.add_default_pull_packages("AID_ROOT"); - - PullAtomPackages* pullAtomPackages = config.add_pull_atom_packages(); - pullAtomPackages->set_atom_id(atom1); - pullAtomPackages->add_packages(app1); - pullAtomPackages->add_packages(app3); - - pullAtomPackages = config.add_pull_atom_packages(); - pullAtomPackages->set_atom_id(atom2); - pullAtomPackages->add_packages(app2); - pullAtomPackages->add_packages("AID_STATSD"); - - MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap, - pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor); - EXPECT_TRUE(metricsManager.isConfigValid()); - - // Update with new allowed log sources. - StatsdConfig newConfig; - newConfig.add_allowed_log_source("AID_ROOT"); - newConfig.add_allowed_log_source(app2); - newConfig.add_default_pull_packages("AID_SYSTEM"); - newConfig.add_default_pull_packages("AID_STATSD"); - - pullAtomPackages = newConfig.add_pull_atom_packages(); - pullAtomPackages->set_atom_id(atom2); - pullAtomPackages->add_packages(app1); - pullAtomPackages->add_packages(app3); - - pullAtomPackages = newConfig.add_pull_atom_packages(); - pullAtomPackages->set_atom_id(atom3); - pullAtomPackages->add_packages(app2); - pullAtomPackages->add_packages("AID_ADB"); - - metricsManager.updateConfig(newConfig, timeBaseSec, timeBaseSec, anomalyAlarmMonitor, - periodicAlarmMonitor); - EXPECT_TRUE(metricsManager.isConfigValid()); - - EXPECT_THAT(metricsManager.mAllowedUid, ElementsAre(AID_ROOT)); - EXPECT_THAT(metricsManager.mAllowedPkg, ElementsAre(app2)); - EXPECT_THAT(metricsManager.mAllowedLogSources, - ContainerEq(unionSet(vector<set<int32_t>>({app2Uids, {AID_ROOT}})))); - const set<int32_t> defaultPullUids = {AID_SYSTEM, AID_STATSD}; - EXPECT_THAT(metricsManager.mDefaultPullUids, ContainerEq(defaultPullUids)); - - vector<int32_t> atom1Uids = metricsManager.getPullAtomUids(atom1); - EXPECT_THAT(atom1Uids, UnorderedElementsAreArray(defaultPullUids)); - - vector<int32_t> atom2Uids = metricsManager.getPullAtomUids(atom2); - EXPECT_THAT(atom2Uids, - UnorderedElementsAreArray(unionSet({defaultPullUids, app1Uids, app3Uids}))); - - vector<int32_t> atom3Uids = metricsManager.getPullAtomUids(atom3); - EXPECT_THAT(atom3Uids, - UnorderedElementsAreArray(unionSet({defaultPullUids, app2Uids, {AID_ADB}}))); -} - -TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom) { - sp<UidMap> uidMap; - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - - StatsdConfig config; - config.add_whitelisted_atom_ids(3); - config.add_whitelisted_atom_ids(4); - - MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap, - pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor); - - LogEvent event(0 /* uid */, 0 /* pid */); - CreateNoValuesLogEvent(&event, 10 /* atom id */, 0 /* timestamp */); - EXPECT_FALSE(metricsManager.checkLogCredentials(event)); - - CreateNoValuesLogEvent(&event, 3 /* atom id */, 0 /* timestamp */); - EXPECT_TRUE(metricsManager.checkLogCredentials(event)); - - CreateNoValuesLogEvent(&event, 4 /* atom id */, 0 /* timestamp */); - EXPECT_TRUE(metricsManager.checkLogCredentials(event)); -} - -TEST(MetricsManagerTest, TestWhitelistedAtomStateTracker) { - sp<UidMap> uidMap; - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - - StatsdConfig config = buildGoodConfig(); - config.add_allowed_log_source("AID_SYSTEM"); - config.add_whitelisted_atom_ids(3); - config.add_whitelisted_atom_ids(4); - - State state; - state.set_id(1); - state.set_atom_id(3); - - *config.add_state() = state; - - config.mutable_count_metric(0)->add_slice_by_state(state.id()); - - StateManager::getInstance().clear(); - - MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap, - pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor); - - EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount()); - EXPECT_FALSE(metricsManager.isConfigValid()); -} - -} // namespace statsd -} // namespace os -} // namespace android - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp deleted file mode 100644 index 1409b621fdf6..000000000000 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ /dev/null @@ -1,1886 +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. - -#include "StatsLogProcessor.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <stdio.h> - -#include "StatsService.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "packages/UidMap.h" -#include "statslog_statsdtest.h" -#include "storage/StorageManager.h" -#include "tests/statsd_test_util.h" - -using namespace android; -using namespace testing; -using ::ndk::SharedRefBase; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoOutputStream; - -#ifdef __ANDROID__ - -/** - * Mock MetricsManager (ByteSize() is called). - */ -class MockMetricsManager : public MetricsManager { -public: - MockMetricsManager() - : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, 1000, new UidMap(), - new StatsPullerManager(), - new AlarmMonitor(10, - [](const shared_ptr<IStatsCompanionService>&, int64_t) {}, - [](const shared_ptr<IStatsCompanionService>&) {}), - new AlarmMonitor(10, - [](const shared_ptr<IStatsCompanionService>&, int64_t) {}, - [](const shared_ptr<IStatsCompanionService>&) {})) { - } - - MOCK_METHOD0(byteSize, size_t()); - - MOCK_METHOD1(dropData, void(const int64_t dropTimeNs)); -}; - -TEST(StatsLogProcessorTest, TestRateLimitByteSize) { - sp<UidMap> m = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - // Construct the processor with a no-op sendBroadcast function that does nothing. - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }, - [](const int&, const vector<int64_t>&) {return true;}); - - MockMetricsManager mockMetricsManager; - - ConfigKey key(100, 12345); - // Expect only the first flush to trigger a check for byte size since the last two are - // rate-limited. - EXPECT_CALL(mockMetricsManager, byteSize()).Times(1); - p.flushIfNecessaryLocked(key, mockMetricsManager); - p.flushIfNecessaryLocked(key, mockMetricsManager); - p.flushIfNecessaryLocked(key, mockMetricsManager); -} - -TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { - sp<UidMap> m = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> subscriberAlarmMonitor; - int broadcastCount = 0; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - return true; - }, - [](const int&, const vector<int64_t>&) {return true;}); - - MockMetricsManager mockMetricsManager; - - ConfigKey key(100, 12345); - EXPECT_CALL(mockMetricsManager, byteSize()) - .Times(1) - .WillRepeatedly(::testing::Return(int( - StatsdStats::kMaxMetricsBytesPerConfig * .95))); - - // Expect only one broadcast despite always returning a size that should trigger broadcast. - p.flushIfNecessaryLocked(key, mockMetricsManager); - EXPECT_EQ(1, broadcastCount); - - // b/73089712 - // This next call to flush should not trigger a broadcast. - // p.mLastByteSizeTimes.clear(); // Force another check for byte size. - // p.flushIfNecessaryLocked(2, key, mockMetricsManager); - // EXPECT_EQ(1, broadcastCount); -} - -TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { - sp<UidMap> m = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> subscriberAlarmMonitor; - int broadcastCount = 0; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - return true; - }, - [](const int&, const vector<int64_t>&) {return true;}); - - MockMetricsManager mockMetricsManager; - - ConfigKey key(100, 12345); - EXPECT_CALL(mockMetricsManager, byteSize()) - .Times(1) - .WillRepeatedly(::testing::Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); - - EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1); - - // Expect to call the onDumpReport and skip the broadcast. - p.flushIfNecessaryLocked(key, mockMetricsManager); - EXPECT_EQ(0, broadcastCount); -} - -StatsdConfig MakeConfig(bool includeMetric) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - if (includeMetric) { - auto appCrashMatcher = CreateProcessCrashAtomMatcher(); - *config.add_atom_matcher() = appCrashMatcher; - auto countMetric = config.add_count_metric(); - countMetric->set_id(StringToId("AppCrashes")); - countMetric->set_what(appCrashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - } - return config; -} - -TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) { - // Setup simple config key corresponding to empty config. - sp<UidMap> m = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")}, - {String16("p1"), String16("p2")}, {String16(""), String16("")}); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> subscriberAlarmMonitor; - int broadcastCount = 0; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - return true; - }, - [](const int&, const vector<int64_t>&) {return true;}); - ConfigKey key(3, 4); - StatsdConfig config = MakeConfig(true); - p.OnConfigUpdated(0, key, config); - - // Expect to get no metrics, but snapshot specified above in uidmap. - vector<uint8_t> bytes; - p.onDumpReport(key, 1, false, true, ADB_DUMP, FAST, &bytes); - - ConfigMetricsReportList output; - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_TRUE(output.reports_size() > 0); - auto uidmap = output.reports(0).uid_map(); - EXPECT_TRUE(uidmap.snapshots_size() > 0); - ASSERT_EQ(2, uidmap.snapshots(0).package_info_size()); -} - -TEST(StatsLogProcessorTest, TestEmptyConfigHasNoUidMap) { - // Setup simple config key corresponding to empty config. - sp<UidMap> m = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")}, - {String16("p1"), String16("p2")}, {String16(""), String16("")}); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> subscriberAlarmMonitor; - int broadcastCount = 0; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - return true; - }, - [](const int&, const vector<int64_t>&) {return true;}); - ConfigKey key(3, 4); - StatsdConfig config = MakeConfig(false); - p.OnConfigUpdated(0, key, config); - - // Expect to get no metrics, but snapshot specified above in uidmap. - vector<uint8_t> bytes; - p.onDumpReport(key, 1, false, true, ADB_DUMP, FAST, &bytes); - - ConfigMetricsReportList output; - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_TRUE(output.reports_size() > 0); - EXPECT_FALSE(output.reports(0).has_uid_map()); -} - -TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) { - // Setup simple config key corresponding to empty config. - sp<UidMap> m = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> subscriberAlarmMonitor; - int broadcastCount = 0; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - return true; - }, - [](const int&, const vector<int64_t>&) {return true;}); - ConfigKey key(3, 4); - StatsdConfig config; - auto annotation = config.add_annotation(); - annotation->set_field_int64(1); - annotation->set_field_int32(2); - config.add_allowed_log_source("AID_ROOT"); - p.OnConfigUpdated(1, key, config); - - // Expect to get no metrics, but snapshot specified above in uidmap. - vector<uint8_t> bytes; - p.onDumpReport(key, 1, false, true, ADB_DUMP, FAST, &bytes); - - ConfigMetricsReportList output; - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_TRUE(output.reports_size() > 0); - auto report = output.reports(0); - ASSERT_EQ(1, report.annotation_size()); - EXPECT_EQ(1, report.annotation(0).field_int64()); - EXPECT_EQ(2, report.annotation(0).field_int32()); -} - -TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) { - // Setup a simple config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = wakelockAcquireMatcher; - - auto countMetric = config.add_count_metric(); - countMetric->set_id(123456); - countMetric->set_what(wakelockAcquireMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - - ConfigKey cfgKey; - sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey); - - std::vector<int> attributionUids = {111}; - std::vector<string> attributionTags = {"App1"}; - std::unique_ptr<LogEvent> event = - CreateAcquireWakelockEvent(2 /*timestamp*/, attributionUids, attributionTags, "wl1"); - processor->OnLogEvent(event.get()); - - vector<uint8_t> bytes; - ConfigMetricsReportList output; - - // Dump report WITHOUT erasing data. - processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST, - &bytes); - output.ParseFromArray(bytes.data(), bytes.size()); - ASSERT_EQ(output.reports_size(), 1); - ASSERT_EQ(output.reports(0).metrics_size(), 1); - ASSERT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1); - - // Dump report WITH erasing data. There should be data since we didn't previously erase it. - processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes); - output.ParseFromArray(bytes.data(), bytes.size()); - ASSERT_EQ(output.reports_size(), 1); - ASSERT_EQ(output.reports(0).metrics_size(), 1); - ASSERT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1); - - // Dump report again. There should be no data since we erased it. - processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes); - output.ParseFromArray(bytes.data(), bytes.size()); - // We don't care whether statsd has a report, as long as it has no count metrics in it. - bool noData = output.reports_size() == 0 || output.reports(0).metrics_size() == 0 || - output.reports(0).metrics(0).count_metrics().data_size() == 0; - EXPECT_TRUE(noData); -} - -TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate) { - // Setup simple config key corresponding to empty config. - sp<UidMap> m = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> subscriberAlarmMonitor; - StatsLogProcessor p( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }, - [](const int&, const vector<int64_t>&) { return true; }); - ConfigKey key(3, 4); - StatsdConfig config = MakeConfig(false); - p.OnConfigUpdated(0, key, config); - EXPECT_NE(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end()); - - config.add_default_pull_packages("AID_STATSD"); - p.OnConfigUpdated(5, key, config); - EXPECT_NE(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end()); - - p.OnConfigRemoved(key); - EXPECT_EQ(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end()); -} - -TEST(StatsLogProcessorTest, InvalidConfigRemoved) { - // Setup simple config key corresponding to empty config. - StatsdStats::getInstance().reset(); - sp<UidMap> m = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")}, - {String16("p1"), String16("p2")}, {String16(""), String16("")}); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> subscriberAlarmMonitor; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }, - [](const int&, const vector<int64_t>&) {return true;}); - ConfigKey key(3, 4); - StatsdConfig config = MakeConfig(true); - p.OnConfigUpdated(0, key, config); - EXPECT_EQ(1, p.mMetricsManagers.size()); - EXPECT_NE(p.mMetricsManagers.find(key), p.mMetricsManagers.end()); - // Cannot assert the size of mConfigStats since it is static and does not get cleared on reset. - EXPECT_NE(StatsdStats::getInstance().mConfigStats.end(), - StatsdStats::getInstance().mConfigStats.find(key)); - EXPECT_EQ(0, StatsdStats::getInstance().mIceBox.size()); - - StatsdConfig invalidConfig = MakeConfig(true); - invalidConfig.clear_allowed_log_source(); - p.OnConfigUpdated(0, key, invalidConfig); - EXPECT_EQ(0, p.mMetricsManagers.size()); - // The current configs should not contain the invalid config. - EXPECT_EQ(StatsdStats::getInstance().mConfigStats.end(), - StatsdStats::getInstance().mConfigStats.find(key)); - // Both "config" and "invalidConfig" should be in the icebox. - EXPECT_EQ(2, StatsdStats::getInstance().mIceBox.size()); - -} - - -TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { - int uid = 1111; - - // Setup a simple config, no activation - StatsdConfig config1; - int64_t cfgId1 = 12341; - config1.set_id(cfgId1); - config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - *config1.add_atom_matcher() = wakelockAcquireMatcher; - - long metricId1 = 1234561; - long metricId2 = 1234562; - auto countMetric1 = config1.add_count_metric(); - countMetric1->set_id(metricId1); - countMetric1->set_what(wakelockAcquireMatcher.id()); - countMetric1->set_bucket(FIVE_MINUTES); - - auto countMetric2 = config1.add_count_metric(); - countMetric2->set_id(metricId2); - countMetric2->set_what(wakelockAcquireMatcher.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - ConfigKey cfgKey1(uid, cfgId1); - - // Add another config, with two metrics, one with activation - StatsdConfig config2; - int64_t cfgId2 = 12342; - config2.set_id(cfgId2); - config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config2.add_atom_matcher() = wakelockAcquireMatcher; - - long metricId3 = 1234561; - long metricId4 = 1234562; - - auto countMetric3 = config2.add_count_metric(); - countMetric3->set_id(metricId3); - countMetric3->set_what(wakelockAcquireMatcher.id()); - countMetric3->set_bucket(FIVE_MINUTES); - - auto countMetric4 = config2.add_count_metric(); - countMetric4->set_id(metricId4); - countMetric4->set_what(wakelockAcquireMatcher.id()); - countMetric4->set_bucket(FIVE_MINUTES); - - auto metric3Activation = config2.add_metric_activation(); - metric3Activation->set_metric_id(metricId3); - metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY); - auto metric3ActivationTrigger = metric3Activation->add_event_activation(); - metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric3ActivationTrigger->set_ttl_seconds(100); - - ConfigKey cfgKey2(uid, cfgId2); - - // Add another config, with two metrics, both with activations - StatsdConfig config3; - int64_t cfgId3 = 12343; - config3.set_id(cfgId3); - config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config3.add_atom_matcher() = wakelockAcquireMatcher; - - long metricId5 = 1234565; - long metricId6 = 1234566; - auto countMetric5 = config3.add_count_metric(); - countMetric5->set_id(metricId5); - countMetric5->set_what(wakelockAcquireMatcher.id()); - countMetric5->set_bucket(FIVE_MINUTES); - - auto countMetric6 = config3.add_count_metric(); - countMetric6->set_id(metricId6); - countMetric6->set_what(wakelockAcquireMatcher.id()); - countMetric6->set_bucket(FIVE_MINUTES); - - auto metric5Activation = config3.add_metric_activation(); - metric5Activation->set_metric_id(metricId5); - metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY); - auto metric5ActivationTrigger = metric5Activation->add_event_activation(); - metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric5ActivationTrigger->set_ttl_seconds(100); - - auto metric6Activation = config3.add_metric_activation(); - metric6Activation->set_metric_id(metricId6); - metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY); - auto metric6ActivationTrigger = metric6Activation->add_event_activation(); - metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric6ActivationTrigger->set_ttl_seconds(200); - - ConfigKey cfgKey3(uid, cfgId3); - - sp<UidMap> m = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> subscriberAlarmMonitor; - vector<int64_t> activeConfigsBroadcast; - - long timeBase1 = 1; - int broadcastCount = 0; - StatsLogProcessor processor( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, timeBase1, - [](const ConfigKey& key) { return true; }, - [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, - const vector<int64_t>& activeConfigs) { - broadcastCount++; - EXPECT_EQ(broadcastUid, uid); - activeConfigsBroadcast.clear(); - activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), - activeConfigs.end()); - return true; - }); - - processor.OnConfigUpdated(1, cfgKey1, config1); - processor.OnConfigUpdated(2, cfgKey2, config2); - processor.OnConfigUpdated(3, cfgKey3, config3); - - ASSERT_EQ(3, processor.mMetricsManagers.size()); - - // Expect the first config and both metrics in it to be active. - auto it = processor.mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor.mMetricsManagers.end()); - auto& metricsManager1 = it->second; - EXPECT_TRUE(metricsManager1->isActive()); - - auto metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer1 = *metricIt; - EXPECT_TRUE(metricProducer1->isActive()); - - metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer2 = *metricIt; - EXPECT_TRUE(metricProducer2->isActive()); - - // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active. - it = processor.mMetricsManagers.find(cfgKey2); - EXPECT_TRUE(it != processor.mMetricsManagers.end()); - auto& metricsManager2 = it->second; - EXPECT_TRUE(metricsManager2->isActive()); - - metricIt = metricsManager2->mAllMetricProducers.begin(); - for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId3) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end()); - auto& metricProducer3 = *metricIt; - EXPECT_FALSE(metricProducer3->isActive()); - - metricIt = metricsManager2->mAllMetricProducers.begin(); - for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId4) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end()); - auto& metricProducer4 = *metricIt; - EXPECT_TRUE(metricProducer4->isActive()); - - // Expect the third config and both metrics in it to be inactive. - it = processor.mMetricsManagers.find(cfgKey3); - EXPECT_TRUE(it != processor.mMetricsManagers.end()); - auto& metricsManager3 = it->second; - EXPECT_FALSE(metricsManager3->isActive()); - - metricIt = metricsManager3->mAllMetricProducers.begin(); - for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId5) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end()); - auto& metricProducer5 = *metricIt; - EXPECT_FALSE(metricProducer5->isActive()); - - metricIt = metricsManager3->mAllMetricProducers.begin(); - for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId6) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end()); - auto& metricProducer6 = *metricIt; - EXPECT_FALSE(metricProducer6->isActive()); - - // No broadcast for active configs should have happened yet. - EXPECT_EQ(broadcastCount, 0); - - // Activate all 3 metrics that were not active. - std::vector<int> attributionUids = {111}; - std::vector<string> attributionTags = {"App1"}; - std::unique_ptr<LogEvent> event = - CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); - processor.OnLogEvent(event.get()); - - // Assert that all 3 configs are active. - EXPECT_TRUE(metricsManager1->isActive()); - EXPECT_TRUE(metricsManager2->isActive()); - EXPECT_TRUE(metricsManager3->isActive()); - - // A broadcast should have happened, and all 3 configs should be active in the broadcast. - EXPECT_EQ(broadcastCount, 1); - ASSERT_EQ(activeConfigsBroadcast.size(), 3); - EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) != - activeConfigsBroadcast.end()); - EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) != - activeConfigsBroadcast.end()); - EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) != - activeConfigsBroadcast.end()); - - // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns. - int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; - processor.SaveActiveConfigsToDisk(shutDownTime); - const int64_t ttl3 = event->GetElapsedTimestampNs() + - metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; - const int64_t ttl5 = event->GetElapsedTimestampNs() + - metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; - const int64_t ttl6 = event->GetElapsedTimestampNs() + - metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; - - // Create a second StatsLogProcessor and push the same 3 configs. - long timeBase2 = 1000; - sp<StatsLogProcessor> processor2 = - CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); - processor2->OnConfigUpdated(timeBase2, cfgKey2, config2); - processor2->OnConfigUpdated(timeBase2, cfgKey3, config3); - - ASSERT_EQ(3, processor2->mMetricsManagers.size()); - - // First config and both metrics are active. - it = processor2->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1001 = it->second; - EXPECT_TRUE(metricsManager1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1001 = *metricIt; - EXPECT_TRUE(metricProducer1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1002 = *metricIt; - EXPECT_TRUE(metricProducer1002->isActive()); - - // Second config is active. Metric 3 is inactive, metric 4 is active. - it = processor2->mMetricsManagers.find(cfgKey2); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1002 = it->second; - EXPECT_TRUE(metricsManager1002->isActive()); - - metricIt = metricsManager1002->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId3) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end()); - auto& metricProducer1003 = *metricIt; - EXPECT_FALSE(metricProducer1003->isActive()); - - metricIt = metricsManager1002->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId4) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end()); - auto& metricProducer1004 = *metricIt; - EXPECT_TRUE(metricProducer1004->isActive()); - - // Config 3 is inactive. both metrics are inactive. - it = processor2->mMetricsManagers.find(cfgKey3); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1003 = it->second; - EXPECT_FALSE(metricsManager1003->isActive()); - ASSERT_EQ(2, metricsManager1003->mAllMetricProducers.size()); - - metricIt = metricsManager1003->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId5) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end()); - auto& metricProducer1005 = *metricIt; - EXPECT_FALSE(metricProducer1005->isActive()); - - metricIt = metricsManager1003->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId6) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end()); - auto& metricProducer1006 = *metricIt; - EXPECT_FALSE(metricProducer1006->isActive()); - - // Assert that all 3 metrics with activation are inactive and that the ttls were properly set. - EXPECT_FALSE(metricProducer1003->isActive()); - const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second; - EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); - EXPECT_EQ(0, activation1003->start_ns); - EXPECT_FALSE(metricProducer1005->isActive()); - const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second; - EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns); - EXPECT_EQ(0, activation1005->start_ns); - EXPECT_FALSE(metricProducer1006->isActive()); - const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second; - EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns); - EXPECT_EQ(0, activation1006->start_ns); - - processor2->LoadActiveConfigsFromDisk(); - - // After loading activations from disk, assert that all 3 metrics are active. - EXPECT_TRUE(metricProducer1003->isActive()); - EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns); - EXPECT_TRUE(metricProducer1005->isActive()); - EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns); - EXPECT_TRUE(metricProducer1006->isActive()); - EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns); - - // Make sure no more broadcasts have happened. - EXPECT_EQ(broadcastCount, 1); -} - -TEST(StatsLogProcessorTest, TestActivationOnBoot) { - int uid = 1111; - - StatsdConfig config1; - config1.set_id(12341); - config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - *config1.add_atom_matcher() = wakelockAcquireMatcher; - - long metricId1 = 1234561; - long metricId2 = 1234562; - auto countMetric1 = config1.add_count_metric(); - countMetric1->set_id(metricId1); - countMetric1->set_what(wakelockAcquireMatcher.id()); - countMetric1->set_bucket(FIVE_MINUTES); - - auto countMetric2 = config1.add_count_metric(); - countMetric2->set_id(metricId2); - countMetric2->set_what(wakelockAcquireMatcher.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - auto metric1Activation = config1.add_metric_activation(); - metric1Activation->set_metric_id(metricId1); - metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); - auto metric1ActivationTrigger = metric1Activation->add_event_activation(); - metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric1ActivationTrigger->set_ttl_seconds(100); - - ConfigKey cfgKey1(uid, 12341); - long timeBase1 = 1; - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); - - ASSERT_EQ(1, processor->mMetricsManagers.size()); - auto it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); - auto& metricsManager1 = it->second; - EXPECT_TRUE(metricsManager1->isActive()); - - auto metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer1 = *metricIt; - EXPECT_FALSE(metricProducer1->isActive()); - - metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer2 = *metricIt; - EXPECT_TRUE(metricProducer2->isActive()); - - const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second; - EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kNotActive, activation1->state); - - std::vector<int> attributionUids = {111}; - std::vector<string> attributionTags = {"App1"}; - std::unique_ptr<LogEvent> event = - CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); - processor->OnLogEvent(event.get()); - - EXPECT_FALSE(metricProducer1->isActive()); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1->state); - - int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; - processor->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_FALSE(metricProducer1->isActive()); - const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC; - - long timeBase2 = 1000; - sp<StatsLogProcessor> processor2 = - CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); - - ASSERT_EQ(1, processor2->mMetricsManagers.size()); - it = processor2->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1001 = it->second; - EXPECT_TRUE(metricsManager1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1001 = *metricIt; - EXPECT_FALSE(metricProducer1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1002 = *metricIt; - EXPECT_TRUE(metricProducer1002->isActive()); - - const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second; - EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); - EXPECT_EQ(0, activation1001->start_ns); - EXPECT_EQ(kNotActive, activation1001->state); - - processor2->LoadActiveConfigsFromDisk(); - - EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns); - EXPECT_EQ(kActive, activation1001->state); -} - -TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { - int uid = 1111; - - // Create config with 2 metrics: - // Metric 1: Activate on boot with 2 activations - // Metric 2: Always active - StatsdConfig config1; - config1.set_id(12341); - config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - *config1.add_atom_matcher() = wakelockAcquireMatcher; - *config1.add_atom_matcher() = screenOnMatcher; - - long metricId1 = 1234561; - long metricId2 = 1234562; - - auto countMetric1 = config1.add_count_metric(); - countMetric1->set_id(metricId1); - countMetric1->set_what(wakelockAcquireMatcher.id()); - countMetric1->set_bucket(FIVE_MINUTES); - - auto countMetric2 = config1.add_count_metric(); - countMetric2->set_id(metricId2); - countMetric2->set_what(wakelockAcquireMatcher.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - auto metric1Activation = config1.add_metric_activation(); - metric1Activation->set_metric_id(metricId1); - metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); - auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); - metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric1ActivationTrigger1->set_ttl_seconds(100); - auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); - metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); - metric1ActivationTrigger2->set_ttl_seconds(200); - - ConfigKey cfgKey1(uid, 12341); - long timeBase1 = 1; - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor->mMetricsManagers.size()); - auto it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); - auto& metricsManager1 = it->second; - EXPECT_TRUE(metricsManager1->isActive()); - - auto metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer1 = *metricIt; - EXPECT_FALSE(metricProducer1->isActive()); - - metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer2 = *metricIt; - EXPECT_TRUE(metricProducer2->isActive()); - - int i = 0; - for (; i < metricsManager1->mAllAtomMatchingTrackers.size(); i++) { - if (metricsManager1->mAllAtomMatchingTrackers[i]->getId() == - metric1ActivationTrigger1->atom_matcher_id()) { - break; - } - } - const auto& activation1 = metricProducer1->mEventActivationMap.at(i); - EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kNotActive, activation1->state); - - i = 0; - for (; i < metricsManager1->mAllAtomMatchingTrackers.size(); i++) { - if (metricsManager1->mAllAtomMatchingTrackers[i]->getId() == - metric1ActivationTrigger2->atom_matcher_id()) { - break; - } - } - const auto& activation2 = metricProducer1->mEventActivationMap.at(i); - EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); - EXPECT_EQ(0, activation2->start_ns); - EXPECT_EQ(kNotActive, activation2->state); - // }}}------------------------------------------------------------------------------ - - // Trigger Activation 1 for Metric 1 - std::vector<int> attributionUids = {111}; - std::vector<string> attributionTags = {"App1"}; - std::unique_ptr<LogEvent> event = - CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); - processor->OnLogEvent(event.get()); - - // Metric 1 is not active; Activation 1 set to kActiveOnBoot - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_FALSE(metricProducer1->isActive()); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1->state); - EXPECT_EQ(0, activation2->start_ns); - EXPECT_EQ(kNotActive, activation2->state); - - EXPECT_TRUE(metricProducer2->isActive()); - // }}}----------------------------------------------------------------------------- - - // Simulate shutdown by saving state to disk - int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; - processor->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_FALSE(metricProducer1->isActive()); - int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC; - - // Simulate device restarted state by creating new instance of StatsLogProcessor with the - // same config. - long timeBase2 = 1000; - sp<StatsLogProcessor> processor2 = - CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor2->mMetricsManagers.size()); - it = processor2->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1001 = it->second; - EXPECT_TRUE(metricsManager1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1001 = *metricIt; - EXPECT_FALSE(metricProducer1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1002 = *metricIt; - EXPECT_TRUE(metricProducer1002->isActive()); - - i = 0; - for (; i < metricsManager1001->mAllAtomMatchingTrackers.size(); i++) { - if (metricsManager1001->mAllAtomMatchingTrackers[i]->getId() == - metric1ActivationTrigger1->atom_matcher_id()) { - break; - } - } - const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i); - EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns); - EXPECT_EQ(0, activation1001_1->start_ns); - EXPECT_EQ(kNotActive, activation1001_1->state); - - i = 0; - for (; i < metricsManager1001->mAllAtomMatchingTrackers.size(); i++) { - if (metricsManager1001->mAllAtomMatchingTrackers[i]->getId() == - metric1ActivationTrigger2->atom_matcher_id()) { - break; - } - } - - const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i); - EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns); - EXPECT_EQ(0, activation1001_2->start_ns); - EXPECT_EQ(kNotActive, activation1001_2->state); - // }}}----------------------------------------------------------------------------------- - - // Load saved state from disk. - processor2->LoadActiveConfigsFromDisk(); - - // Metric 1 active; Activation 1 is active, Activation 2 is not active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); - EXPECT_EQ(kActive, activation1001_1->state); - EXPECT_EQ(0, activation1001_2->start_ns); - EXPECT_EQ(kNotActive, activation1001_2->state); - - EXPECT_TRUE(metricProducer1002->isActive()); - // }}}-------------------------------------------------------------------------------- - - // Trigger Activation 2 for Metric 1. - auto screenOnEvent = - CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON); - processor2->OnLogEvent(screenOnEvent.get()); - - // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); - EXPECT_EQ(kActive, activation1001_1->state); - EXPECT_EQ(0, activation1001_2->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1001_2->state); - - EXPECT_TRUE(metricProducer1002->isActive()); - // }}}--------------------------------------------------------------------------- - - // Simulate shutdown by saving state to disk - shutDownTime = timeBase2 + 50 * NS_PER_SEC; - processor2->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_TRUE(metricProducer1002->isActive()); - ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime; - int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC; - - // Simulate device restarted state by creating new instance of StatsLogProcessor with the - // same config. - long timeBase3 = timeBase2 + 120 * NS_PER_SEC; - sp<StatsLogProcessor> processor3 = - CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor3->mMetricsManagers.size()); - it = processor3->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor3->mMetricsManagers.end()); - auto& metricsManagerTimeBase3 = it->second; - EXPECT_TRUE(metricsManagerTimeBase3->isActive()); - - metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); - for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); - auto& metricProducerTimeBase3_1 = *metricIt; - EXPECT_FALSE(metricProducerTimeBase3_1->isActive()); - - metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); - for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); - auto& metricProducerTimeBase3_2 = *metricIt; - EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); - - i = 0; - for (; i < metricsManagerTimeBase3->mAllAtomMatchingTrackers.size(); i++) { - if (metricsManagerTimeBase3->mAllAtomMatchingTrackers[i]->getId() == - metric1ActivationTrigger1->atom_matcher_id()) { - break; - } - } - const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i); - EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns); - EXPECT_EQ(0, activationTimeBase3_1->start_ns); - EXPECT_EQ(kNotActive, activationTimeBase3_1->state); - - i = 0; - for (; i < metricsManagerTimeBase3->mAllAtomMatchingTrackers.size(); i++) { - if (metricsManagerTimeBase3->mAllAtomMatchingTrackers[i]->getId() == - metric1ActivationTrigger2->atom_matcher_id()) { - break; - } - } - - const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i); - EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns); - EXPECT_EQ(0, activationTimeBase3_2->start_ns); - EXPECT_EQ(kNotActive, activationTimeBase3_2->state); - - EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); - // }}}---------------------------------------------------------------------------------- - - // Load saved state from disk. - processor3->LoadActiveConfigsFromDisk(); - - // Metric 1 active: Activation 1 is active, Activation 2 is active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); - EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns); - EXPECT_EQ(kActive, activationTimeBase3_1->state); - EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); - EXPECT_EQ(kActive, activationTimeBase3_2->state); - - EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); - // }}}------------------------------------------------------------------------------- - - // Trigger Activation 2 for Metric 1 again. - screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC, - android::view::DISPLAY_STATE_ON); - processor3->OnLogEvent(screenOnEvent.get()); - - // Metric 1 active; Activation 1 is not active, Activation 2 is set to active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); - EXPECT_EQ(kNotActive, activationTimeBase3_1->state); - EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); - EXPECT_EQ(kActive, activationTimeBase3_2->state); - - EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); - // }}}--------------------------------------------------------------------------- - - // Simulate shutdown by saving state to disk. - shutDownTime = timeBase3 + 500 * NS_PER_SEC; - processor3->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_TRUE(metricProducer1002->isActive()); - ttl1 = timeBase3 + ttl1 - shutDownTime; - ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime; - - // Simulate device restarted state by creating new instance of StatsLogProcessor with the - // same config. - long timeBase4 = timeBase3 + 600 * NS_PER_SEC; - sp<StatsLogProcessor> processor4 = - CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor4->mMetricsManagers.size()); - it = processor4->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor4->mMetricsManagers.end()); - auto& metricsManagerTimeBase4 = it->second; - EXPECT_TRUE(metricsManagerTimeBase4->isActive()); - - metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin(); - for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end()); - auto& metricProducerTimeBase4_1 = *metricIt; - EXPECT_FALSE(metricProducerTimeBase4_1->isActive()); - - metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin(); - for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end()); - auto& metricProducerTimeBase4_2 = *metricIt; - EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); - - i = 0; - for (; i < metricsManagerTimeBase4->mAllAtomMatchingTrackers.size(); i++) { - if (metricsManagerTimeBase4->mAllAtomMatchingTrackers[i]->getId() == - metric1ActivationTrigger1->atom_matcher_id()) { - break; - } - } - const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i); - EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns); - EXPECT_EQ(0, activationTimeBase4_1->start_ns); - EXPECT_EQ(kNotActive, activationTimeBase4_1->state); - - i = 0; - for (; i < metricsManagerTimeBase4->mAllAtomMatchingTrackers.size(); i++) { - if (metricsManagerTimeBase4->mAllAtomMatchingTrackers[i]->getId() == - metric1ActivationTrigger2->atom_matcher_id()) { - break; - } - } - - const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i); - EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns); - EXPECT_EQ(0, activationTimeBase4_2->start_ns); - EXPECT_EQ(kNotActive, activationTimeBase4_2->state); - - EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); - // }}}---------------------------------------------------------------------------------- - - // Load saved state from disk. - processor4->LoadActiveConfigsFromDisk(); - - // Metric 1 active: Activation 1 is not active, Activation 2 is not active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_FALSE(metricProducerTimeBase4_1->isActive()); - EXPECT_EQ(kNotActive, activationTimeBase4_1->state); - EXPECT_EQ(kNotActive, activationTimeBase4_2->state); - - EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); - // }}}------------------------------------------------------------------------------- -} - -TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) { - int uid = 1111; - - // Create config with 2 metrics: - // Metric 1: Activate on boot with 2 activations - // Metric 2: Always active - StatsdConfig config1; - config1.set_id(12341); - config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - *config1.add_atom_matcher() = wakelockAcquireMatcher; - *config1.add_atom_matcher() = screenOnMatcher; - - long metricId1 = 1234561; - long metricId2 = 1234562; - - auto countMetric1 = config1.add_count_metric(); - countMetric1->set_id(metricId1); - countMetric1->set_what(wakelockAcquireMatcher.id()); - countMetric1->set_bucket(FIVE_MINUTES); - - auto countMetric2 = config1.add_count_metric(); - countMetric2->set_id(metricId2); - countMetric2->set_what(wakelockAcquireMatcher.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - auto metric1Activation = config1.add_metric_activation(); - metric1Activation->set_metric_id(metricId1); - metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); - auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); - metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric1ActivationTrigger1->set_ttl_seconds(100); - auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); - metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); - metric1ActivationTrigger2->set_ttl_seconds(200); - metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); - - ConfigKey cfgKey1(uid, 12341); - long timeBase1 = 1; - sp<StatsLogProcessor> processor1 = - CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor1->mMetricsManagers.size()); - auto it = processor1->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor1->mMetricsManagers.end()); - auto& metricsManager1 = it->second; - EXPECT_TRUE(metricsManager1->isActive()); - - ASSERT_EQ(metricsManager1->mAllMetricProducers.size(), 2); - // We assume that the index of a MetricProducer within the mAllMetricProducers - // array follows the order in which metrics are added to the config. - auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0]; - EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1); - EXPECT_FALSE(metricProducer1_1->isActive()); // inactive due to associated MetricActivation - - auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1]; - EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2); - EXPECT_TRUE(metricProducer1_2->isActive()); - - ASSERT_EQ(metricProducer1_1->mEventActivationMap.size(), 2); - // The key in mEventActivationMap is the index of the associated atom matcher. We assume - // that matchers are indexed in the order that they are added to the config. - const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0); - EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns); - EXPECT_EQ(0, activation1_1_1->start_ns); - EXPECT_EQ(kNotActive, activation1_1_1->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType); - - const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1); - EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns); - EXPECT_EQ(0, activation1_1_2->start_ns); - EXPECT_EQ(kNotActive, activation1_1_2->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType); - // }}}------------------------------------------------------------------------------ - - // Trigger Activation 1 for Metric 1 - std::vector<int> attributionUids = {111}; - std::vector<string> attributionTags = {"App1"}; - std::unique_ptr<LogEvent> event = - CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); - processor1->OnLogEvent(event.get()); - - // Metric 1 is not active; Activation 1 set to kActiveOnBoot - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_FALSE(metricProducer1_1->isActive()); - EXPECT_EQ(0, activation1_1_1->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1_1_1->state); - EXPECT_EQ(0, activation1_1_2->start_ns); - EXPECT_EQ(kNotActive, activation1_1_2->state); - - EXPECT_TRUE(metricProducer1_2->isActive()); - // }}}----------------------------------------------------------------------------- - - // Simulate shutdown by saving state to disk - int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; - processor1->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_FALSE(metricProducer1_1->isActive()); - - // Simulate device restarted state by creating new instance of StatsLogProcessor with the - // same config. - long timeBase2 = 1000; - sp<StatsLogProcessor> processor2 = - CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor2->mMetricsManagers.size()); - it = processor2->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager2 = it->second; - EXPECT_TRUE(metricsManager2->isActive()); - - ASSERT_EQ(metricsManager2->mAllMetricProducers.size(), 2); - // We assume that the index of a MetricProducer within the mAllMetricProducers - // array follows the order in which metrics are added to the config. - auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0]; - EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1); - EXPECT_FALSE(metricProducer2_1->isActive()); - - auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1]; - EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2); - EXPECT_TRUE(metricProducer2_2->isActive()); - - ASSERT_EQ(metricProducer2_1->mEventActivationMap.size(), 2); - // The key in mEventActivationMap is the index of the associated atom matcher. We assume - // that matchers are indexed in the order that they are added to the config. - const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0); - EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns); - EXPECT_EQ(0, activation2_1_1->start_ns); - EXPECT_EQ(kNotActive, activation2_1_1->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType); - - const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1); - EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns); - EXPECT_EQ(0, activation2_1_2->start_ns); - EXPECT_EQ(kNotActive, activation2_1_2->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType); - // }}}----------------------------------------------------------------------------------- - - // Load saved state from disk. - processor2->LoadActiveConfigsFromDisk(); - - // Metric 1 active; Activation 1 is active, Activation 2 is not active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer2_1->isActive()); - int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC; - EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns); - EXPECT_EQ(kActive, activation2_1_1->state); - EXPECT_EQ(0, activation2_1_2->start_ns); - EXPECT_EQ(kNotActive, activation2_1_2->state); - - EXPECT_TRUE(metricProducer2_2->isActive()); - // }}}-------------------------------------------------------------------------------- - - // Trigger Activation 2 for Metric 1. - auto screenOnEvent = - CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON); - processor2->OnLogEvent(screenOnEvent.get()); - - // Metric 1 active; Activation 1 is active, Activation 2 is active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer2_1->isActive()); - EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns); - EXPECT_EQ(kActive, activation2_1_1->state); - EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns); - EXPECT_EQ(kActive, activation2_1_2->state); - - EXPECT_TRUE(metricProducer2_2->isActive()); - // }}}--------------------------------------------------------------------------- - - // Simulate shutdown by saving state to disk - shutDownTime = timeBase2 + 50 * NS_PER_SEC; - processor2->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_TRUE(metricProducer2_1->isActive()); - EXPECT_TRUE(metricProducer2_2->isActive()); - ttl1 -= shutDownTime - timeBase2; - int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - - (shutDownTime - screenOnEvent->GetElapsedTimestampNs()); - - // Simulate device restarted state by creating new instance of StatsLogProcessor with the - // same config. - long timeBase3 = timeBase2 + 120 * NS_PER_SEC; - sp<StatsLogProcessor> processor3 = - CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor3->mMetricsManagers.size()); - it = processor3->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor3->mMetricsManagers.end()); - auto& metricsManager3 = it->second; - EXPECT_TRUE(metricsManager3->isActive()); - - ASSERT_EQ(metricsManager3->mAllMetricProducers.size(), 2); - // We assume that the index of a MetricProducer within the mAllMetricProducers - // array follows the order in which metrics are added to the config. - auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0]; - EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1); - EXPECT_FALSE(metricProducer3_1->isActive()); - - auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1]; - EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2); - EXPECT_TRUE(metricProducer3_2->isActive()); - - ASSERT_EQ(metricProducer3_1->mEventActivationMap.size(), 2); - // The key in mEventActivationMap is the index of the associated atom matcher. We assume - // that matchers are indexed in the order that they are added to the config. - const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0); - EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns); - EXPECT_EQ(0, activation3_1_1->start_ns); - EXPECT_EQ(kNotActive, activation3_1_1->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType); - - const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1); - EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns); - EXPECT_EQ(0, activation3_1_2->start_ns); - EXPECT_EQ(kNotActive, activation3_1_2->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType); - // }}}---------------------------------------------------------------------------------- - - // Load saved state from disk. - processor3->LoadActiveConfigsFromDisk(); - - // Metric 1 active: Activation 1 is active, Activation 2 is active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer3_1->isActive()); - EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns); - EXPECT_EQ(kActive, activation3_1_1->state); - EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns); - EXPECT_EQ(kActive, activation3_1_2->state); - - EXPECT_TRUE(metricProducer3_2->isActive()); - // }}}------------------------------------------------------------------------------- - - // Trigger Activation 2 for Metric 1 again. - screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC, - android::view::DISPLAY_STATE_ON); - processor3->OnLogEvent(screenOnEvent.get()); - - // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire), - // Activation 2 is set to active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer3_1->isActive()); - EXPECT_EQ(kNotActive, activation3_1_1->state); - EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns); - EXPECT_EQ(kActive, activation3_1_2->state); - - EXPECT_TRUE(metricProducer3_2->isActive()); - // }}}--------------------------------------------------------------------------- -} - -TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { - int uid = 9876; - long configId = 12341; - - // Create config with 3 metrics: - // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate. - // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate. - // Metric 3: Always active - StatsdConfig config1; - config1.set_id(configId); - config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto jobStartMatcher = CreateStartScheduledJobAtomMatcher(); - auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher(); - *config1.add_atom_matcher() = wakelockAcquireMatcher; - *config1.add_atom_matcher() = screenOnMatcher; - *config1.add_atom_matcher() = jobStartMatcher; - *config1.add_atom_matcher() = jobFinishMatcher; - - long metricId1 = 1234561; - long metricId2 = 1234562; - long metricId3 = 1234563; - - auto countMetric1 = config1.add_count_metric(); - countMetric1->set_id(metricId1); - countMetric1->set_what(wakelockAcquireMatcher.id()); - countMetric1->set_bucket(FIVE_MINUTES); - - auto countMetric2 = config1.add_count_metric(); - countMetric2->set_id(metricId2); - countMetric2->set_what(wakelockAcquireMatcher.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - auto countMetric3 = config1.add_count_metric(); - countMetric3->set_id(metricId3); - countMetric3->set_what(wakelockAcquireMatcher.id()); - countMetric3->set_bucket(FIVE_MINUTES); - - // Metric 1 activates on boot for wakelock acquire, immediately for screen on. - auto metric1Activation = config1.add_metric_activation(); - metric1Activation->set_metric_id(metricId1); - auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); - metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric1ActivationTrigger1->set_ttl_seconds(100); - metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); - auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); - metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); - metric1ActivationTrigger2->set_ttl_seconds(200); - metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); - - // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish. - auto metric2Activation = config1.add_metric_activation(); - metric2Activation->set_metric_id(metricId2); - auto metric2ActivationTrigger1 = metric2Activation->add_event_activation(); - metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id()); - metric2ActivationTrigger1->set_ttl_seconds(100); - metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); - auto metric2ActivationTrigger2 = metric2Activation->add_event_activation(); - metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id()); - metric2ActivationTrigger2->set_ttl_seconds(200); - metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); - - // Send the config. - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - string serialized = config1.SerializeAsString(); - service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()}); - - // Make sure the config is stored on disk. Otherwise, we will not reset on system server death. - StatsdConfig tmpConfig; - ConfigKey cfgKey1(uid, configId); - EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig)); - - // Metric 1 is not active. - // Metric 2 is not active. - // Metric 3 is active. - // {{{--------------------------------------------------------------------------- - sp<StatsLogProcessor> processor = service->mProcessor; - ASSERT_EQ(1, processor->mMetricsManagers.size()); - auto it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); - auto& metricsManager1 = it->second; - EXPECT_TRUE(metricsManager1->isActive()); - ASSERT_EQ(3, metricsManager1->mAllMetricProducers.size()); - - auto& metricProducer1 = metricsManager1->mAllMetricProducers[0]; - EXPECT_EQ(metricId1, metricProducer1->getMetricId()); - EXPECT_FALSE(metricProducer1->isActive()); - - auto& metricProducer2 = metricsManager1->mAllMetricProducers[1]; - EXPECT_EQ(metricId2, metricProducer2->getMetricId()); - EXPECT_FALSE(metricProducer2->isActive()); - - auto& metricProducer3 = metricsManager1->mAllMetricProducers[2]; - EXPECT_EQ(metricId3, metricProducer3->getMetricId()); - EXPECT_TRUE(metricProducer3->isActive()); - - // Check event activations. - ASSERT_EQ(metricsManager1->mAllAtomMatchingTrackers.size(), 4); - EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[0]->getId(), - metric1ActivationTrigger1->atom_matcher_id()); - const auto& activation1 = metricProducer1->mEventActivationMap.at(0); - EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kNotActive, activation1->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType); - - EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[1]->getId(), - metric1ActivationTrigger2->atom_matcher_id()); - const auto& activation2 = metricProducer1->mEventActivationMap.at(1); - EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); - EXPECT_EQ(0, activation2->start_ns); - EXPECT_EQ(kNotActive, activation2->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType); - - EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[2]->getId(), - metric2ActivationTrigger1->atom_matcher_id()); - const auto& activation3 = metricProducer2->mEventActivationMap.at(2); - EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns); - EXPECT_EQ(0, activation3->start_ns); - EXPECT_EQ(kNotActive, activation3->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType); - - EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[3]->getId(), - metric2ActivationTrigger2->atom_matcher_id()); - const auto& activation4 = metricProducer2->mEventActivationMap.at(3); - EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns); - EXPECT_EQ(0, activation4->start_ns); - EXPECT_EQ(kNotActive, activation4->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType); - // }}}------------------------------------------------------------------------------ - - // Trigger Activation 1 for Metric 1. Should activate on boot. - // Trigger Activation 4 for Metric 2. Should activate immediately. - int64_t configAddedTimeNs = metricsManager1->mLastReportTimeNs; - std::vector<int> attributionUids = {111}; - std::vector<string> attributionTags = {"App1"}; - std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent( - 1 + configAddedTimeNs, attributionUids, attributionTags, "wl1"); - processor->OnLogEvent(event1.get()); - - std::unique_ptr<LogEvent> event2 = CreateFinishScheduledJobEvent( - 2 + configAddedTimeNs, attributionUids, attributionTags, "finish1"); - processor->OnLogEvent(event2.get()); - - // Metric 1 is not active; Activation 1 set to kActiveOnBoot - // Metric 2 is active. Activation 4 set to kActive - // Metric 3 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_FALSE(metricProducer1->isActive()); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1->state); - EXPECT_EQ(0, activation2->start_ns); - EXPECT_EQ(kNotActive, activation2->state); - - EXPECT_TRUE(metricProducer2->isActive()); - EXPECT_EQ(0, activation3->start_ns); - EXPECT_EQ(kNotActive, activation3->state); - EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns); - EXPECT_EQ(kActive, activation4->state); - - EXPECT_TRUE(metricProducer3->isActive()); - // }}}----------------------------------------------------------------------------- - - // Can't fake time with StatsService. - // Lets get a time close to the system server death time and make sure it's sane. - int64_t approximateSystemServerDeath = getElapsedRealtimeNs(); - EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs); - EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs); - - // System server dies. - service->statsCompanionServiceDiedImpl(); - - // We should have a new metrics manager. Lets get it and ensure activation status is restored. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor->mMetricsManagers.size()); - it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); - auto& metricsManager2 = it->second; - EXPECT_TRUE(metricsManager2->isActive()); - ASSERT_EQ(3, metricsManager2->mAllMetricProducers.size()); - - auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0]; - EXPECT_EQ(metricId1, metricProducer1001->getMetricId()); - EXPECT_FALSE(metricProducer1001->isActive()); - - auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1]; - EXPECT_EQ(metricId2, metricProducer1002->getMetricId()); - EXPECT_TRUE(metricProducer1002->isActive()); - - auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2]; - EXPECT_EQ(metricId3, metricProducer1003->getMetricId()); - EXPECT_TRUE(metricProducer1003->isActive()); - - // Check event activations. - // Activation 1 is kActiveOnBoot. - // Activation 2 and 3 are not active. - // Activation 4 is active. - ASSERT_EQ(metricsManager2->mAllAtomMatchingTrackers.size(), 4); - EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[0]->getId(), - metric1ActivationTrigger1->atom_matcher_id()); - const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0); - EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); - EXPECT_EQ(0, activation1001->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1001->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType); - - EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[1]->getId(), - metric1ActivationTrigger2->atom_matcher_id()); - const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1); - EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns); - EXPECT_EQ(0, activation1002->start_ns); - EXPECT_EQ(kNotActive, activation1002->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType); - - EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[2]->getId(), - metric2ActivationTrigger1->atom_matcher_id()); - const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2); - EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); - EXPECT_EQ(0, activation1003->start_ns); - EXPECT_EQ(kNotActive, activation1003->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType); - - EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[3]->getId(), - metric2ActivationTrigger2->atom_matcher_id()); - const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3); - EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns); - EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns); - EXPECT_EQ(kActive, activation1004->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType); - // }}}------------------------------------------------------------------------------ - - // Clear the data stored on disk as a result of the system server death. - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true, ADB_DUMP, FAST, - &buffer); -} - -TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUid) { - int hostUid = 20; - int isolatedUid = 30; - uint64_t eventTimeNs = 12355; - int atomId = 89; - int field1 = 90; - int field2 = 28; - sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); - ConfigKey cfgKey; - StatsdConfig config = MakeConfig(false); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); - - shared_ptr<LogEvent> logEvent = makeUidLogEvent(atomId, eventTimeNs, hostUid, field1, field2); - - processor->OnLogEvent(logEvent.get()); - - const vector<FieldValue>* actualFieldValues = &logEvent->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value); -} - -TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUid) { - int hostUid = 20; - int isolatedUid = 30; - uint64_t eventTimeNs = 12355; - int atomId = 89; - int field1 = 90; - int field2 = 28; - sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); - ConfigKey cfgKey; - StatsdConfig config = MakeConfig(false); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); - - shared_ptr<LogEvent> logEvent = - makeUidLogEvent(atomId, eventTimeNs, isolatedUid, field1, field2); - - processor->OnLogEvent(logEvent.get()); - - const vector<FieldValue>* actualFieldValues = &logEvent->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value); -} - -TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUidAttributionChain) { - int hostUid = 20; - int isolatedUid = 30; - uint64_t eventTimeNs = 12355; - int atomId = 89; - int field1 = 90; - int field2 = 28; - sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); - ConfigKey cfgKey; - StatsdConfig config = MakeConfig(false); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); - - shared_ptr<LogEvent> logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {hostUid, 200}, - {"tag1", "tag2"}, field1, field2); - - processor->OnLogEvent(logEvent.get()); - - const vector<FieldValue>* actualFieldValues = &logEvent->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value); -} - -TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUidAttributionChain) { - int hostUid = 20; - int isolatedUid = 30; - uint64_t eventTimeNs = 12355; - int atomId = 89; - int field1 = 90; - int field2 = 28; - sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); - ConfigKey cfgKey; - StatsdConfig config = MakeConfig(false); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); - - shared_ptr<LogEvent> logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {isolatedUid, 200}, - {"tag1", "tag2"}, field1, field2); - - processor->OnLogEvent(logEvent.get()); - - const vector<FieldValue>* actualFieldValues = &logEvent->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value); -} - -TEST(StatsLogProcessorTest, TestDumpReportWithoutErasingDataDoesNotUpdateTimestamp) { - int hostUid = 20; - int isolatedUid = 30; - sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); - ConfigKey key(3, 4); - - // TODO: All tests should not persist state on disk. This removes any reports that were present. - ProtoOutputStream proto; - StorageManager::appendConfigMetricsReport(key, &proto, /*erase data=*/true, /*isAdb=*/false); - - StatsdConfig config = MakeConfig(false); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(1, 1, config, key, nullptr, 0, mockUidMap); - vector<uint8_t> bytes; - - int64_t dumpTime1Ns = 1 * NS_PER_SEC; - processor->onDumpReport(key, dumpTime1Ns, false /* include_current_bucket */, - true /* erase_data */, ADB_DUMP, FAST, &bytes); - - ConfigMetricsReportList output; - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_EQ(output.reports_size(), 1); - EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime1Ns); - - int64_t dumpTime2Ns = 5 * NS_PER_SEC; - processor->onDumpReport(key, dumpTime2Ns, false /* include_current_bucket */, - false /* erase_data */, ADB_DUMP, FAST, &bytes); - - // Check that the dump report without clearing data is successful. - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_EQ(output.reports_size(), 1); - EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime2Ns); - EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns); - - int64_t dumpTime3Ns = 10 * NS_PER_SEC; - processor->onDumpReport(key, dumpTime3Ns, false /* include_current_bucket */, - true /* erase_data */, ADB_DUMP, FAST, &bytes); - - // Check that the previous dump report that didn't clear data did not overwrite the first dump's - // timestamps. - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_EQ(output.reports_size(), 1); - EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime3Ns); - EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns); - -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp deleted file mode 100644 index cc38c4a4067a..000000000000 --- a/cmds/statsd/tests/StatsService_test.cpp +++ /dev/null @@ -1,104 +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. - -#include "StatsService.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -#include <android/binder_interface_utils.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <stdio.h> - -using namespace android; -using namespace testing; - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoOutputStream; -using ::ndk::SharedRefBase; - -#ifdef __ANDROID__ - -TEST(StatsServiceTest, TestAddConfig_simple) { - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - StatsdConfig config; - config.set_id(12345); - string serialized = config.SerializeAsString(); - - EXPECT_TRUE( - service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); -} - -TEST(StatsServiceTest, TestAddConfig_empty) { - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - string serialized = ""; - - EXPECT_TRUE( - service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); -} - -TEST(StatsServiceTest, TestAddConfig_invalid) { - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - string serialized = "Invalid config!"; - - EXPECT_FALSE( - service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); -} - -TEST(StatsServiceTest, TestGetUidFromArgs) { - Vector<String8> args; - args.push(String8("-1")); - args.push(String8("0")); - args.push(String8("1")); - args.push(String8("a1")); - args.push(String8("")); - - int32_t uid; - - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - service->mEngBuild = true; - - // "-1" - EXPECT_FALSE(service->getUidFromArgs(args, 0, uid)); - - // "0" - EXPECT_TRUE(service->getUidFromArgs(args, 1, uid)); - EXPECT_EQ(0, uid); - - // "1" - EXPECT_TRUE(service->getUidFromArgs(args, 2, uid)); - EXPECT_EQ(1, uid); - - // "a1" - EXPECT_FALSE(service->getUidFromArgs(args, 3, uid)); - - // "" - EXPECT_FALSE(service->getUidFromArgs(args, 4, uid)); - - // For a non-userdebug, uid "1" cannot be impersonated. - service->mEngBuild = false; - EXPECT_FALSE(service->getUidFromArgs(args, 2, uid)); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp deleted file mode 100644 index 33bdc64333e0..000000000000 --- a/cmds/statsd/tests/UidMap_test.cpp +++ /dev/null @@ -1,426 +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. - -#include "packages/UidMap.h" -#include "StatsLogProcessor.h" -#include "config/ConfigKey.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "hash.h" -#include "statslog_statsdtest.h" -#include "statsd_test_util.h" - -#include <android/util/ProtoOutputStream.h> -#include <gtest/gtest.h> - -#include <stdio.h> - -using namespace android; - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoOutputStream; -using android::util::ProtoReader; - -#ifdef __ANDROID__ -const string kApp1 = "app1.sharing.1"; -const string kApp2 = "app2.sharing.1"; - -TEST(UidMapTest, TestIsolatedUID) { - sp<UidMap> m = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> subscriberAlarmMonitor; - // Construct the processor with a no-op sendBroadcast function that does nothing. - StatsLogProcessor p( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }, - [](const int&, const vector<int64_t>&) { return true; }); - - std::unique_ptr<LogEvent> addEvent = CreateIsolatedUidChangedEvent( - 1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 1 /*is_create*/); - EXPECT_EQ(101, m->getHostUidOrSelf(101)); - p.OnLogEvent(addEvent.get()); - EXPECT_EQ(100, m->getHostUidOrSelf(101)); - - std::unique_ptr<LogEvent> removeEvent = CreateIsolatedUidChangedEvent( - 1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 0 /*is_create*/); - p.OnLogEvent(removeEvent.get()); - EXPECT_EQ(101, m->getHostUidOrSelf(101)); -} - -TEST(UidMapTest, TestMatching) { - UidMap m; - vector<int32_t> uids; - vector<int64_t> versions; - vector<String16> apps; - vector<String16> versionStrings; - vector<String16> installers; - - uids.push_back(1000); - uids.push_back(1000); - versionStrings.push_back(String16("v1")); - versionStrings.push_back(String16("v1")); - installers.push_back(String16("")); - installers.push_back(String16("")); - apps.push_back(String16(kApp1.c_str())); - apps.push_back(String16(kApp2.c_str())); - versions.push_back(4); - versions.push_back(5); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - EXPECT_TRUE(m.hasApp(1000, kApp1)); - EXPECT_TRUE(m.hasApp(1000, kApp2)); - EXPECT_FALSE(m.hasApp(1000, "not.app")); - - std::set<string> name_set = m.getAppNamesFromUid(1000u, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 2u); - EXPECT_TRUE(name_set.find(kApp1) != name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - - name_set = m.getAppNamesFromUid(12345, true /* returnNormalized */); - EXPECT_TRUE(name_set.empty()); -} - -TEST(UidMapTest, TestAddAndRemove) { - UidMap m; - vector<int32_t> uids; - vector<int64_t> versions; - vector<String16> apps; - vector<String16> versionStrings; - vector<String16> installers; - - uids.push_back(1000); - uids.push_back(1000); - versionStrings.push_back(String16("v1")); - versionStrings.push_back(String16("v1")); - installers.push_back(String16("")); - installers.push_back(String16("")); - apps.push_back(String16(kApp1.c_str())); - apps.push_back(String16(kApp2.c_str())); - versions.push_back(4); - versions.push_back(5); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 2u); - EXPECT_TRUE(name_set.find(kApp1) != name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - - // Update the app1 version. - m.updateApp(2, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16("")); - EXPECT_EQ(40, m.getAppVersion(1000, kApp1)); - - name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 2u); - EXPECT_TRUE(name_set.find(kApp1) != name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - - m.removeApp(3, String16(kApp1.c_str()), 1000); - EXPECT_FALSE(m.hasApp(1000, kApp1)); - EXPECT_TRUE(m.hasApp(1000, kApp2)); - name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 1u); - EXPECT_TRUE(name_set.find(kApp1) == name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - - // Remove app2. - m.removeApp(4, String16(kApp2.c_str()), 1000); - EXPECT_FALSE(m.hasApp(1000, kApp1)); - EXPECT_FALSE(m.hasApp(1000, kApp2)); - name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - EXPECT_TRUE(name_set.empty()); -} - -TEST(UidMapTest, TestUpdateApp) { - UidMap m; - m.updateMap(1, {1000, 1000}, {4, 5}, {String16("v4"), String16("v5")}, - {String16(kApp1.c_str()), String16(kApp2.c_str())}, {String16(""), String16("")}); - std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 2u); - EXPECT_TRUE(name_set.find(kApp1) != name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - - // Adds a new name for uid 1000. - m.updateApp(2, String16("NeW_aPP1_NAmE"), 1000, 40, String16("v40"), String16("")); - name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 3u); - EXPECT_TRUE(name_set.find(kApp1) != name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end()); - EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end()); - - // This name is also reused by another uid 2000. - m.updateApp(3, String16("NeW_aPP1_NAmE"), 2000, 1, String16("v1"), String16("")); - name_set = m.getAppNamesFromUid(2000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 1u); - EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end()); - EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end()); -} - -static void protoOutputStreamToUidMapping(ProtoOutputStream* proto, UidMapping* results) { - vector<uint8_t> bytes; - bytes.resize(proto->size()); - size_t pos = 0; - sp<ProtoReader> reader = proto->data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((bytes)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - results->ParseFromArray(bytes.data(), bytes.size()); -} - -// Test that uid map returns at least one snapshot even if we already obtained -// this snapshot from a previous call to getData. -TEST(UidMapTest, TestOutputIncludesAtLeastOneSnapshot) { - UidMap m; - // Initialize single config key. - ConfigKey config1(1, StringToId("config1")); - m.OnConfigUpdated(config1); - vector<int32_t> uids; - vector<int64_t> versions; - vector<String16> apps; - vector<String16> versionStrings; - vector<String16> installers; - uids.push_back(1000); - apps.push_back(String16(kApp2.c_str())); - versionStrings.push_back(String16("v1")); - installers.push_back(String16("")); - versions.push_back(5); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - // Set the last timestamp for this config key to be newer. - m.mLastUpdatePerConfigKey[config1] = 2; - - ProtoOutputStream proto; - m.appendUidMap(3, config1, nullptr, true, true, &proto); - - // Check there's still a uidmap attached this one. - UidMapping results; - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - EXPECT_EQ("v1", results.snapshots(0).package_info(0).version_string()); -} - -TEST(UidMapTest, TestRemovedAppRetained) { - UidMap m; - // Initialize single config key. - ConfigKey config1(1, StringToId("config1")); - m.OnConfigUpdated(config1); - vector<int32_t> uids; - vector<int64_t> versions; - vector<String16> versionStrings; - vector<String16> installers; - vector<String16> apps; - uids.push_back(1000); - apps.push_back(String16(kApp2.c_str())); - versions.push_back(5); - versionStrings.push_back(String16("v5")); - installers.push_back(String16("")); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - m.removeApp(2, String16(kApp2.c_str()), 1000); - - ProtoOutputStream proto; - m.appendUidMap(3, config1, nullptr, true, true, &proto); - - // Snapshot should still contain this item as deleted. - UidMapping results; - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots(0).package_info_size()); - EXPECT_EQ(true, results.snapshots(0).package_info(0).deleted()); -} - -TEST(UidMapTest, TestRemovedAppOverGuardrail) { - UidMap m; - // Initialize single config key. - ConfigKey config1(1, StringToId("config1")); - m.OnConfigUpdated(config1); - vector<int32_t> uids; - vector<int64_t> versions; - vector<String16> versionStrings; - vector<String16> installers; - vector<String16> apps; - const int maxDeletedApps = StatsdStats::kMaxDeletedAppsInUidMap; - for (int j = 0; j < maxDeletedApps + 10; j++) { - uids.push_back(j); - apps.push_back(String16(kApp1.c_str())); - versions.push_back(j); - versionStrings.push_back(String16("v")); - installers.push_back(String16("")); - } - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - // First, verify that we have the expected number of items. - UidMapping results; - ProtoOutputStream proto; - m.appendUidMap(3, config1, nullptr, true, true, &proto); - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(maxDeletedApps + 10, results.snapshots(0).package_info_size()); - - // Now remove all the apps. - m.updateMap(1, uids, versions, versionStrings, apps, installers); - for (int j = 0; j < maxDeletedApps + 10; j++) { - m.removeApp(4, String16(kApp1.c_str()), j); - } - - proto.clear(); - m.appendUidMap(5, config1, nullptr, true, true, &proto); - // Snapshot drops the first nine items. - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(maxDeletedApps, results.snapshots(0).package_info_size()); -} - -TEST(UidMapTest, TestClearingOutput) { - UidMap m; - - ConfigKey config1(1, StringToId("config1")); - ConfigKey config2(1, StringToId("config2")); - - m.OnConfigUpdated(config1); - - vector<int32_t> uids; - vector<int64_t> versions; - vector<String16> versionStrings; - vector<String16> installers; - vector<String16> apps; - uids.push_back(1000); - uids.push_back(1000); - apps.push_back(String16(kApp1.c_str())); - apps.push_back(String16(kApp2.c_str())); - versions.push_back(4); - versions.push_back(5); - versionStrings.push_back(String16("v4")); - versionStrings.push_back(String16("v5")); - installers.push_back(String16("")); - installers.push_back(String16("")); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - ProtoOutputStream proto; - m.appendUidMap(2, config1, nullptr, true, true, &proto); - UidMapping results; - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - - // We have to keep at least one snapshot in memory at all times. - proto.clear(); - m.appendUidMap(2, config1, nullptr, true, true, &proto); - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - - // Now add another configuration. - m.OnConfigUpdated(config2); - m.updateApp(5, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16("")); - ASSERT_EQ(1U, m.mChanges.size()); - proto.clear(); - m.appendUidMap(6, config1, nullptr, true, true, &proto); - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - ASSERT_EQ(1, results.changes_size()); - ASSERT_EQ(1U, m.mChanges.size()); - - // Add another delta update. - m.updateApp(7, String16(kApp2.c_str()), 1001, 41, String16("v41"), String16("")); - ASSERT_EQ(2U, m.mChanges.size()); - - // We still can't remove anything. - proto.clear(); - m.appendUidMap(8, config1, nullptr, true, true, &proto); - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - ASSERT_EQ(1, results.changes_size()); - ASSERT_EQ(2U, m.mChanges.size()); - - proto.clear(); - m.appendUidMap(9, config2, nullptr, true, true, &proto); - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - ASSERT_EQ(2, results.changes_size()); - // At this point both should be cleared. - ASSERT_EQ(0U, m.mChanges.size()); -} - -TEST(UidMapTest, TestMemoryComputed) { - UidMap m; - - ConfigKey config1(1, StringToId("config1")); - m.OnConfigUpdated(config1); - - size_t startBytes = m.mBytesUsed; - vector<int32_t> uids; - vector<int64_t> versions; - vector<String16> apps; - vector<String16> versionStrings; - vector<String16> installers; - uids.push_back(1000); - apps.push_back(String16(kApp1.c_str())); - versions.push_back(1); - versionStrings.push_back(String16("v1")); - installers.push_back(String16("")); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - m.updateApp(3, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16("")); - - ProtoOutputStream proto; - vector<uint8_t> bytes; - m.appendUidMap(2, config1, nullptr, true, true, &proto); - size_t prevBytes = m.mBytesUsed; - - m.appendUidMap(4, config1, nullptr, true, true, &proto); - EXPECT_TRUE(m.mBytesUsed < prevBytes); -} - -TEST(UidMapTest, TestMemoryGuardrail) { - UidMap m; - string buf; - - ConfigKey config1(1, StringToId("config1")); - m.OnConfigUpdated(config1); - - size_t startBytes = m.mBytesUsed; - vector<int32_t> uids; - vector<int64_t> versions; - vector<String16> versionStrings; - vector<String16> installers; - vector<String16> apps; - for (int i = 0; i < 100; i++) { - uids.push_back(1); - buf = "EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY." + to_string(i); - apps.push_back(String16(buf.c_str())); - versions.push_back(1); - versionStrings.push_back(String16("v1")); - installers.push_back(String16("")); - } - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2, - String16("v2"), String16("")); - ASSERT_EQ(1U, m.mChanges.size()); - - // Now force deletion by limiting the memory to hold one delta change. - m.maxBytesOverride = 120; // Since the app string alone requires >45 characters. - m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4, - String16("v4"), String16("")); - ASSERT_EQ(1U, m.mChanges.size()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp deleted file mode 100644 index 64ea219c8465..000000000000 --- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// 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. - -#include "src/anomaly/AlarmTracker.h" - -#include <gtest/gtest.h> -#include <log/log_time.h> -#include <stdio.h> -#include <vector> - -using namespace testing; -using android::sp; -using std::set; -using std::shared_ptr; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -const ConfigKey kConfigKey(0, 12345); - -TEST(AlarmTrackerTest, TestTriggerTimestamp) { - sp<AlarmMonitor> subscriberAlarmMonitor = - new AlarmMonitor(100, - [](const shared_ptr<IStatsCompanionService>&, int64_t){}, - [](const shared_ptr<IStatsCompanionService>&){}); - Alarm alarm; - alarm.set_offset_millis(15 * MS_PER_SEC); - alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr - int64_t startMillis = 100000000 * MS_PER_SEC; - int64_t nextAlarmTime = startMillis / MS_PER_SEC + 15; - AlarmTracker tracker(startMillis, startMillis, alarm, kConfigKey, subscriberAlarmMonitor); - - EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); - - uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10; - std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarmSet = - subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); - EXPECT_TRUE(firedAlarmSet.empty()); - tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); - EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); - EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); - - currentTimeSec = startMillis / MS_PER_SEC + 7000; - nextAlarmTime = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60; - firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); - ASSERT_EQ(firedAlarmSet.size(), 1u); - tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); - EXPECT_TRUE(firedAlarmSet.empty()); - EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); - EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); - - // Alarm fires exactly on time. - currentTimeSec = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60; - nextAlarmTime = startMillis / MS_PER_SEC + 15 + 3 * 60 * 60; - firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); - ASSERT_EQ(firedAlarmSet.size(), 1u); - tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); - EXPECT_TRUE(firedAlarmSet.empty()); - EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); - EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); - - // Alarm fires exactly 1 period late. - currentTimeSec = startMillis / MS_PER_SEC + 15 + 4 * 60 * 60; - nextAlarmTime = startMillis / MS_PER_SEC + 15 + 5 * 60 * 60; - firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); - ASSERT_EQ(firedAlarmSet.size(), 1u); - tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); - EXPECT_TRUE(firedAlarmSet.empty()); - EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); - EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp deleted file mode 100644 index 0cc8af16c782..000000000000 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ /dev/null @@ -1,408 +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. - -#include "src/anomaly/AnomalyTracker.h" - -#include <gtest/gtest.h> -#include <math.h> -#include <stdio.h> - -#include <vector> - -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -const ConfigKey kConfigKey(0, 12345); - -MetricDimensionKey getMockMetricDimensionKey(int key, string value) { - int pos[] = {key, 0, 0}; - HashableDimensionKey dim; - dim.addValue(FieldValue(Field(1, pos, 0), Value(value))); - return MetricDimensionKey(dim, DEFAULT_DIMENSION_KEY); -} - -void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list, - std::shared_ptr<DimToValMap> bucket) { - for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) { - (*bucket)[itr->first] += itr->second; - } -} - -std::shared_ptr<DimToValMap> MockBucket( - const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list) { - std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>(); - AddValueToBucket(key_value_pair_list, bucket); - return bucket; -} - -// Returns the value, for the given key, in that bucket, or 0 if not present. -int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket, - const MetricDimensionKey& key) { - const auto& itr = bucket->find(key); - if (itr != bucket->end()) { - return itr->second; - } - return 0; -} - -// Returns true if keys in trueList are detected as anomalies and keys in falseList are not. -bool detectAnomaliesPass(AnomalyTracker& tracker, - const int64_t& bucketNum, - const std::shared_ptr<DimToValMap>& currentBucket, - const std::set<const MetricDimensionKey>& trueList, - const std::set<const MetricDimensionKey>& falseList) { - for (const MetricDimensionKey& key : trueList) { - if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) { - return false; - } - } - for (const MetricDimensionKey& key : falseList) { - if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) { - return false; - } - } - return true; -} - -// Calls tracker.detectAndDeclareAnomaly on each key in the bucket. -void detectAndDeclareAnomalies(AnomalyTracker& tracker, - const int64_t& bucketNum, - const std::shared_ptr<DimToValMap>& bucket, - const int64_t& eventTimestamp) { - for (const auto& kv : *bucket) { - tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first, - kv.second); - } -} - -// Asserts that the refractory time for each key in timestamps is the corresponding -// timestamp (in ns) + refractoryPeriodSec. -// If a timestamp value is negative, instead asserts that the refractory period is inapplicable -// (either non-existant or already past). -void checkRefractoryTimes(AnomalyTracker& tracker, - const int64_t& currTimestampNs, - const int32_t& refractoryPeriodSec, - const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) { - for (const auto& kv : timestamps) { - if (kv.second < 0) { - // Make sure that, if there is a refractory period, it is already past. - EXPECT_LT(tracker.getRefractoryPeriodEndsSec(kv.first) * NS_PER_SEC, - (uint64_t)currTimestampNs) - << "Failure was at currTimestampNs " << currTimestampNs; - } else { - EXPECT_EQ(tracker.getRefractoryPeriodEndsSec(kv.first), - std::ceil(1.0 * kv.second / NS_PER_SEC) + refractoryPeriodSec) - << "Failure was at currTimestampNs " << currTimestampNs; - } - } -} - -TEST(AnomalyTrackerTest, TestConsecutiveBuckets) { - const int64_t bucketSizeNs = 30 * NS_PER_SEC; - const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC; - Alert alert; - alert.set_num_buckets(3); - alert.set_refractory_period_secs(refractoryPeriodSec); - alert.set_trigger_if_sum_gt(2); - - AnomalyTracker anomalyTracker(alert, kConfigKey); - MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a"); - MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b"); - MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c"); - - int64_t eventTimestamp0 = 10 * NS_PER_SEC; - int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC; - int64_t eventTimestamp2 = 2 * bucketSizeNs + 12 * NS_PER_SEC; - int64_t eventTimestamp3 = 3 * bucketSizeNs + 13 * NS_PER_SEC; - int64_t eventTimestamp4 = 4 * bucketSizeNs + 14 * NS_PER_SEC; - int64_t eventTimestamp5 = 5 * bucketSizeNs + 5 * NS_PER_SEC; - int64_t eventTimestamp6 = 6 * bucketSizeNs + 16 * NS_PER_SEC; - - std::shared_ptr<DimToValMap> bucket0 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}}); - std::shared_ptr<DimToValMap> bucket1 = MockBucket({{keyA, 1}}); - std::shared_ptr<DimToValMap> bucket2 = MockBucket({{keyB, 1}}); - std::shared_ptr<DimToValMap> bucket3 = MockBucket({{keyA, 2}}); - std::shared_ptr<DimToValMap> bucket4 = MockBucket({{keyB, 5}}); - std::shared_ptr<DimToValMap> bucket5 = MockBucket({{keyA, 2}}); - std::shared_ptr<DimToValMap> bucket6 = MockBucket({{keyA, 2}}); - - // Start time with no events. - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL); - - // Event from bucket #0 occurs. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 0, bucket0, {}, {keyA, keyB, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 0, bucket0, eventTimestamp1); - checkRefractoryTimes(anomalyTracker, eventTimestamp0, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}}); - - // Adds past bucket #0 - anomalyTracker.addPastBucket(bucket0, 0); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL); - - // Event from bucket #1 occurs. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1); - checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}}); - - // Adds past bucket #0 again. The sum does not change. - anomalyTracker.addPastBucket(bucket0, 0); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1 + 1); - checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}}); - - // Adds past bucket #1. - anomalyTracker.addPastBucket(bucket1, 1); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - - // Event from bucket #2 occurs. New anomaly on keyB. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2); - checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}}); - - // Adds past bucket #1 again. Nothing changes. - anomalyTracker.addPastBucket(bucket1, 1); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - // Event from bucket #2 occurs (again). - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2 + 1); - checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}}); - - // Adds past bucket #2. - anomalyTracker.addPastBucket(bucket2, 2); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - - // Event from bucket #3 occurs. New anomaly on keyA. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 3, bucket3, {keyA}, {keyB, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 3, bucket3, eventTimestamp3); - checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec, - {{keyA, eventTimestamp3}, {keyB, eventTimestamp2}, {keyC, -1}}); - - // Adds bucket #3. - anomalyTracker.addPastBucket(bucket3, 3L); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - - // Event from bucket #4 occurs. New anomaly on keyB. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 4, bucket4, {keyB}, {keyA, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 4, bucket4, eventTimestamp4); - checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec, - {{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}}); - - // Adds bucket #4. - anomalyTracker.addPastBucket(bucket4, 4); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL); - - // Event from bucket #5 occurs. New anomaly on keyA, which is still in refractory. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 5, bucket5, {keyA, keyB}, {keyC})); - detectAndDeclareAnomalies(anomalyTracker, 5, bucket5, eventTimestamp5); - checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec, - {{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}}); - - // Adds bucket #5. - anomalyTracker.addPastBucket(bucket5, 5); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL); - - // Event from bucket #6 occurs. New anomaly on keyA, which is now out of refractory. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 6, bucket6, {keyA, keyB}, {keyC})); - detectAndDeclareAnomalies(anomalyTracker, 6, bucket6, eventTimestamp6); - checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec, - {{keyA, eventTimestamp6}, {keyB, eventTimestamp4}, {keyC, -1}}); -} - -TEST(AnomalyTrackerTest, TestSparseBuckets) { - const int64_t bucketSizeNs = 30 * NS_PER_SEC; - const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC; - Alert alert; - alert.set_num_buckets(3); - alert.set_refractory_period_secs(refractoryPeriodSec); - alert.set_trigger_if_sum_gt(2); - - AnomalyTracker anomalyTracker(alert, kConfigKey); - MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a"); - MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b"); - MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c"); - MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d"); - MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e"); - - std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}}); - std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}}); - std::shared_ptr<DimToValMap> bucket18 = MockBucket({{keyB, 1}, {keyC, 1}}); - std::shared_ptr<DimToValMap> bucket20 = MockBucket({{keyB, 3}, {keyC, 1}}); - std::shared_ptr<DimToValMap> bucket25 = MockBucket({{keyD, 1}}); - std::shared_ptr<DimToValMap> bucket28 = MockBucket({{keyE, 2}}); - - int64_t eventTimestamp1 = bucketSizeNs * 8 + 1; - int64_t eventTimestamp2 = bucketSizeNs * 15 + 11; - int64_t eventTimestamp3 = bucketSizeNs * 17 + 1; - int64_t eventTimestamp4 = bucketSizeNs * 19 + 2; - int64_t eventTimestamp5 = bucketSizeNs * 24 + 3; - int64_t eventTimestamp6 = bucketSizeNs * 27 + 3; - - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 9, bucket9, {}, {keyA, keyB, keyC, keyD})); - detectAndDeclareAnomalies(anomalyTracker, 9, bucket9, eventTimestamp1); - checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Add past bucket #9 - anomalyTracker.addPastBucket(bucket9, 9); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 16, bucket16, {keyB}, {keyA, keyC, keyD})); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L); - detectAndDeclareAnomalies(anomalyTracker, 16, bucket16, eventTimestamp2); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L); - checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Add past bucket #16 - anomalyTracker.addPastBucket(bucket16, 16); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 18, bucket18, {keyB}, {keyA, keyC, keyD})); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL); - // Within refractory period. - detectAndDeclareAnomalies(anomalyTracker, 18, bucket18, eventTimestamp3); - checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL); - - // Add past bucket #18 - anomalyTracker.addPastBucket(bucket18, 18); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD})); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4); - checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Add bucket #18 again. Nothing changes. - anomalyTracker.addPastBucket(bucket18, 18); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD})); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4 + 1); - // Within refractory period. - checkRefractoryTimes(anomalyTracker, eventTimestamp4 + 1, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Add past bucket #20 - anomalyTracker.addPastBucket(bucket20, 20); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 3LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 25, bucket25, {}, {keyA, keyB, keyC, keyD})); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - detectAndDeclareAnomalies(anomalyTracker, 25, bucket25, eventTimestamp5); - checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Add past bucket #25 - anomalyTracker.addPastBucket(bucket25, 25); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyD), 1LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {}, - {keyA, keyB, keyC, keyD, keyE})); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Updates current bucket #28. - (*bucket28)[keyE] = 5; - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {keyE}, - {keyA, keyB, keyC, keyD})); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6 + 7); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, eventTimestamp6 + 7}}); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp deleted file mode 100644 index 1d501fd5a87c..000000000000 --- a/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp +++ /dev/null @@ -1,167 +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. - -#include "condition/condition_util.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -#include <gtest/gtest.h> - -#include <stdio.h> -#include <vector> - -using namespace android::os::statsd; -using std::vector; - -#ifdef __ANDROID__ - -TEST(ConditionTrackerTest, TestUnknownCondition) { - LogicalOperation operation = LogicalOperation::AND; - - vector<int> children; - children.push_back(0); - children.push_back(1); - children.push_back(2); - - vector<ConditionState> conditionResults; - conditionResults.push_back(ConditionState::kUnknown); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kTrue); - - EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults), - ConditionState::kUnknown); -} - -TEST(ConditionTrackerTest, TestAndCondition) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::AND; - - vector<int> children; - children.push_back(0); - children.push_back(1); - children.push_back(2); - - vector<ConditionState> conditionResults; - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kTrue); - - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kTrue); - - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); -} - -TEST(ConditionTrackerTest, TestOrCondition) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::OR; - - vector<int> children; - children.push_back(0); - children.push_back(1); - children.push_back(2); - - vector<ConditionState> conditionResults; - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kTrue); - - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kFalse); - - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); -} - -TEST(ConditionTrackerTest, TestNotCondition) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NOT; - - vector<int> children; - children.push_back(0); - - vector<ConditionState> conditionResults; - conditionResults.push_back(ConditionState::kTrue); - - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kFalse); - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); - - children.clear(); - conditionResults.clear(); - EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults), - ConditionState::kUnknown); -} - -TEST(ConditionTrackerTest, TestNandCondition) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NAND; - - vector<int> children; - children.push_back(0); - children.push_back(1); - - vector<ConditionState> conditionResults; - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kFalse); - - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kFalse); - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kTrue); - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); -} - -TEST(ConditionTrackerTest, TestNorCondition) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NOR; - - vector<int> children; - children.push_back(0); - children.push_back(1); - - vector<ConditionState> conditionResults; - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kFalse); - - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kFalse); - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kTrue); - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/condition/ConditionTimer_test.cpp b/cmds/statsd/tests/condition/ConditionTimer_test.cpp deleted file mode 100644 index 46dc9a9d381f..000000000000 --- a/cmds/statsd/tests/condition/ConditionTimer_test.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/condition/ConditionTimer.h" - -#include <gtest/gtest.h> -#include <stdio.h> - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -static int64_t time_base = 10; -static int64_t ct_start_time = 200; - -TEST(ConditionTimerTest, TestTimer_Inital_False) { - ConditionTimer timer(false, time_base); - EXPECT_EQ(false, timer.mCondition); - EXPECT_EQ(0, timer.mTimerNs); - - EXPECT_EQ(0, timer.newBucketStart(ct_start_time)); - EXPECT_EQ(0, timer.mTimerNs); - - timer.onConditionChanged(true, ct_start_time + 5); - EXPECT_EQ(ct_start_time + 5, timer.mLastConditionChangeTimestampNs); - EXPECT_EQ(true, timer.mCondition); - - EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100)); - EXPECT_EQ(ct_start_time + 100, timer.mLastConditionChangeTimestampNs); - EXPECT_EQ(true, timer.mCondition); -} - -TEST(ConditionTimerTest, TestTimer_Inital_True) { - ConditionTimer timer(true, time_base); - EXPECT_EQ(true, timer.mCondition); - EXPECT_EQ(0, timer.mTimerNs); - - EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time)); - EXPECT_EQ(true, timer.mCondition); - EXPECT_EQ(0, timer.mTimerNs); - EXPECT_EQ(ct_start_time, timer.mLastConditionChangeTimestampNs); - - timer.onConditionChanged(false, ct_start_time + 5); - EXPECT_EQ(5, timer.mTimerNs); - - EXPECT_EQ(5, timer.newBucketStart(ct_start_time + 100)); - EXPECT_EQ(0, timer.mTimerNs); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp deleted file mode 100644 index 8998b5f98df5..000000000000 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ /dev/null @@ -1,741 +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. - -#include "src/condition/SimpleConditionTracker.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <stdio.h> -#include <vector> -#include <numeric> - -using std::map; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -namespace { - -const ConfigKey kConfigKey(0, 12345); - -const int ATTRIBUTION_NODE_FIELD_ID = 1; -const int ATTRIBUTION_UID_FIELD_ID = 1; -const int TAG_ID = 1; -const uint64_t protoHash = 0x123456789; - -SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse, - bool outputSlicedUid, Position position) { - SimplePredicate simplePredicate; - simplePredicate.set_start(StringToId("WAKE_LOCK_ACQUIRE")); - simplePredicate.set_stop(StringToId("WAKE_LOCK_RELEASE")); - simplePredicate.set_stop_all(StringToId("RELEASE_ALL")); - if (outputSlicedUid) { - simplePredicate.mutable_dimensions()->set_field(TAG_ID); - simplePredicate.mutable_dimensions()->add_child()->set_field(ATTRIBUTION_NODE_FIELD_ID); - simplePredicate.mutable_dimensions()->mutable_child(0)->set_position(position); - simplePredicate.mutable_dimensions()->mutable_child(0)->add_child()->set_field( - ATTRIBUTION_UID_FIELD_ID); - } - - simplePredicate.set_count_nesting(countNesting); - simplePredicate.set_initial_value(defaultFalse ? SimplePredicate_InitialValue_FALSE - : SimplePredicate_InitialValue_UNKNOWN); - return simplePredicate; -} - -void makeWakeLockEvent(LogEvent* logEvent, uint32_t atomId, uint64_t timestamp, - const vector<int>& uids, const string& wl, int acquire) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - vector<std::string> tags(uids.size()); // vector of empty strings - writeAttribution(statsEvent, uids, tags); - - AStatsEvent_writeString(statsEvent, wl.c_str()); - AStatsEvent_writeInt32(statsEvent, acquire); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -} // anonymous namespace - - -std::map<int64_t, HashableDimensionKey> getWakeLockQueryKey( - const Position position, - const std::vector<int> &uids, const string& conditionName) { - std::map<int64_t, HashableDimensionKey> outputKeyMap; - std::vector<int> uid_indexes; - int pos[] = {1, 1, 1}; - int depth = 2; - Field field(1, pos, depth); - switch(position) { - case Position::FIRST: - uid_indexes.push_back(0); - break; - case Position::LAST: - uid_indexes.push_back(uids.size() - 1); - field.setField(0x02018001); - break; - case Position::ANY: - uid_indexes.resize(uids.size()); - std::iota(uid_indexes.begin(), uid_indexes.end(), 0); - field.setField(0x02010001); - break; - default: - break; - } - - for (const int idx : uid_indexes) { - Value value((int32_t)uids[idx]); - HashableDimensionKey dim; - dim.addValue(FieldValue(field, value)); - outputKeyMap[StringToId(conditionName)] = dim; - } - return outputKeyMap; -} - -TEST(SimpleConditionTrackerTest, TestNonSlicedInitialValueFalse) { - SimplePredicate simplePredicate; - simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); - simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); - simplePredicate.set_count_nesting(false); - simplePredicate.set_initial_value(SimplePredicate_InitialValue_FALSE); - - unordered_map<int64_t, int> trackerNameIndexMap; - trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; - trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash, - 0 /*tracker index*/, simplePredicate, - trackerNameIndexMap); - - ConditionKey queryKey; - vector<sp<ConditionTracker>> allPredicates; - vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); - - // Check that initial condition is false. - conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - - vector<MatchingState> matcherState; - vector<bool> changedCache(1, false); - - // Matched stop event. - // Check that condition is still false. - unique_ptr<LogEvent> screenOffEvent = - CreateScreenStateChangedEvent(/*timestamp=*/50, android::view::DISPLAY_STATE_OFF); - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); // On matcher not matched - matcherState.push_back(MatchingState::kMatched); // Off matcher matched - conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.evaluateCondition(*screenOffEvent, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); - - // Matched start event. - // Check that condition has changed to true. - unique_ptr<LogEvent> screenOnEvent = - CreateScreenStateChangedEvent(/*timestamp=*/100, android::view::DISPLAY_STATE_ON); - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); // On matcher matched - matcherState.push_back(MatchingState::kNotMatched); // Off matcher not matched - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(*screenOnEvent, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); -} - -TEST(SimpleConditionTrackerTest, TestNonSlicedInitialValueUnknown) { - SimplePredicate simplePredicate; - simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); - simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); - simplePredicate.set_count_nesting(false); - simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN); - - unordered_map<int64_t, int> trackerNameIndexMap; - trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; - trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash, - 0 /*tracker index*/, simplePredicate, - trackerNameIndexMap); - - ConditionKey queryKey; - vector<sp<ConditionTracker>> allPredicates; - vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); - - // Check that initial condition is unknown. - conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); - EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]); - - vector<MatchingState> matcherState; - vector<bool> changedCache(1, false); - - // Matched stop event. - // Check that condition is changed to false. - unique_ptr<LogEvent> screenOffEvent = - CreateScreenStateChangedEvent(/*timestamp=*/50, android::view::DISPLAY_STATE_OFF); - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); // On matcher not matched - matcherState.push_back(MatchingState::kMatched); // Off matcher matched - conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.evaluateCondition(*screenOffEvent, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); - - // Matched start event. - // Check that condition has changed to true. - unique_ptr<LogEvent> screenOnEvent = - CreateScreenStateChangedEvent(/*timestamp=*/100, android::view::DISPLAY_STATE_ON); - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); // On matcher matched - matcherState.push_back(MatchingState::kNotMatched); // Off matcher not matched - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(*screenOnEvent, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); -} - -TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { - SimplePredicate simplePredicate; - simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); - simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); - simplePredicate.set_count_nesting(false); - simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN); - - unordered_map<int64_t, int> trackerNameIndexMap; - trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; - trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash, - 0 /*tracker index*/, simplePredicate, - trackerNameIndexMap); - EXPECT_FALSE(conditionTracker.isSliced()); - - // 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); - 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); - // not matched start or stop. condition doesn't change - EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); - - // prepare a case for match start. - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - // now condition should change to true. - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); - - // match nothing. - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); - - // the case for match stop. - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - - // condition changes to false. - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); - - // match stop again. - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - // condition should still be false. not changed. - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); -} - -TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { - std::vector<sp<ConditionTracker>> allConditions; - SimplePredicate simplePredicate; - simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); - simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); - simplePredicate.set_count_nesting(true); - - unordered_map<int64_t, int> trackerNameIndexMap; - trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; - trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash, - 0 /*condition tracker index*/, simplePredicate, - trackerNameIndexMap); - EXPECT_FALSE(conditionTracker.isSliced()); - - // 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; - matcherState.push_back(MatchingState::kMatched); - 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(ConditionState::kTrue, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); - - // prepare for another matched start. - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); - - // ONE MATCHED STOP - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - // result should still be true - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); - - // ANOTHER MATCHED STOP - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); -} - -TEST(SimpleConditionTrackerTest, TestSlicedCondition) { - 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_UID2"; - - 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), protoHash, - 0 /*condition tracker index*/, simplePredicate, - trackerNameIndexMap); - - std::vector<int> uids = {111, 222, 333}; - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids, "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) { - ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - } else { - ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); - } - EXPECT_TRUE(changedCache[0]); - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u); - EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - } else { - EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), - uids.size()); - } - - // Now test query - const auto queryKey = getWakeLockQueryKey(position, uids, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; - - conditionTracker.isConditionMet(queryKey, allPredicates, false, 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, uids, "wl2", /*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]); - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - } else { - ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); - } - EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); - EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - - - // wake lock 1 release - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/0); - 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 wake lock 2 is still held for this uid - EXPECT_FALSE(changedCache[0]); - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - } else { - ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); - } - EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); - EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/0); - 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); - ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); - EXPECT_TRUE(changedCache[0]); - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u); - EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); - } else { - EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), - uids.size()); - } - - // query again - 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), protoHash, - 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); - - ASSERT_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); - ASSERT_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), protoHash, - 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) { - ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - } else { - ASSERT_EQ(uids1.size(), conditionTracker.mSlicedConditionState.size()); - } - EXPECT_TRUE(changedCache[0]); - { - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_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) { - ASSERT_EQ(2UL, conditionTracker.mSlicedConditionState.size()); - } else { - ASSERT_EQ(uids1.size() + uids2.size(), conditionTracker.mSlicedConditionState.size()); - } - EXPECT_TRUE(changedCache[0]); - { - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_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]); - ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); - { - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_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 -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp b/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp deleted file mode 100644 index 93b278388a1b..000000000000 --- a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp +++ /dev/null @@ -1,92 +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. - -#include <gtest/gtest.h> - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto alarm = config.add_alarm(); - alarm->set_id(123456); - alarm->set_offset_millis(TimeUnitToBucketSizeInMillis(TEN_MINUTES)); - alarm->set_period_millis(TimeUnitToBucketSizeInMillis(ONE_HOUR)); - - alarm = config.add_alarm(); - alarm->set_id(654321); - alarm->set_offset_millis(TimeUnitToBucketSizeInMillis(FIVE_MINUTES)); - alarm->set_period_millis(TimeUnitToBucketSizeInMillis(THIRTY_MINUTES)); - return config; -} - -} // namespace - -TEST(AlarmE2eTest, TestMultipleAlarms) { - auto config = CreateStatsdConfig(); - int64_t bucketStartTimeNs = 10000000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(2u, processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers.size()); - - auto alarmTracker1 = processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers[0]; - auto alarmTracker2 = processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers[1]; - - int64_t alarmTimestampSec0 = bucketStartTimeNs / NS_PER_SEC + 10 * 60; - int64_t alarmTimestampSec1 = bucketStartTimeNs / NS_PER_SEC + 5 * 60; - EXPECT_EQ(alarmTimestampSec0, alarmTracker1->getAlarmTimestampSec()); - EXPECT_EQ(alarmTimestampSec1, alarmTracker2->getAlarmTimestampSec()); - - // Alarm fired. - const int64_t alarmFiredTimestampSec0 = alarmTimestampSec1 + 5; - auto alarmSet = processor->getPeriodicAlarmMonitor()->popSoonerThan( - static_cast<uint32_t>(alarmFiredTimestampSec0)); - ASSERT_EQ(1u, alarmSet.size()); - processor->onPeriodicAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet); - EXPECT_EQ(alarmTimestampSec0, alarmTracker1->getAlarmTimestampSec()); - EXPECT_EQ(alarmTimestampSec1 + 30 * 60, alarmTracker2->getAlarmTimestampSec()); - - // Alarms fired very late. - const int64_t alarmFiredTimestampSec1 = alarmTimestampSec0 + 2 * 60 * 60 + 125; - alarmSet = processor->getPeriodicAlarmMonitor()->popSoonerThan( - static_cast<uint32_t>(alarmFiredTimestampSec1)); - ASSERT_EQ(2u, alarmSet.size()); - processor->onPeriodicAlarmFired(alarmFiredTimestampSec1 * NS_PER_SEC, alarmSet); - EXPECT_EQ(alarmTimestampSec0 + 60 * 60 * 3, alarmTracker1->getAlarmTimestampSec()); - EXPECT_EQ(alarmTimestampSec1 + 30 * 60 * 5, alarmTracker2->getAlarmTimestampSec()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp deleted file mode 100644 index af9436b98ec8..000000000000 --- a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp +++ /dev/null @@ -1,390 +0,0 @@ -// 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. - -#include <gtest/gtest.h> - -#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig(int num_buckets, int threshold, int refractory_period_sec) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - - *config.add_atom_matcher() = wakelockAcquireMatcher; - - auto countMetric = config.add_count_metric(); - countMetric->set_id(123456); - countMetric->set_what(wakelockAcquireMatcher.id()); - *countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - countMetric->set_bucket(FIVE_MINUTES); - - auto alert = config.add_alert(); - alert->set_id(StringToId("alert")); - alert->set_metric_id(123456); - alert->set_num_buckets(num_buckets); - alert->set_refractory_period_secs(refractory_period_sec); - alert->set_trigger_if_sum_gt(threshold); - return config; -} - -} // namespace - -TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) { - const int num_buckets = 1; - const int threshold = 3; - const int refractory_period_sec = 10; - auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); - const uint64_t alert_id = config.alert(0).id(); - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_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(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), - Value((int32_t)111)); - HashableDimensionKey whatKey1({fieldValue1}); - MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); - - FieldValue fieldValue2(Field(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; - const int refractory_period_sec = 10; - auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); - const uint64_t alert_id = config.alert(0).id(); - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_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(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)); -} - -TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written) { - const int num_buckets = 1; - const int threshold = 0; - const int refractory_period_sec = 86400 * 365; // 1 year - auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); - const int64_t alert_id = config.alert(0).id(); - - int64_t bucketStartTimeNs = 10000000000; - - int configUid = 2000; - int64_t configId = 1000; - ConfigKey cfgKey(configUid, configId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - metadata::StatsMetadataList result; - int64_t mockWallClockNs = 1584991200 * NS_PER_SEC; - int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC; - processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result); - - ASSERT_EQ(result.stats_metadata_size(), 0); -} - -TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk) { - const int num_buckets = 1; - const int threshold = 0; - const int refractory_period_sec = 86400 * 365; // 1 year - auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); - const int64_t alert_id = config.alert(0).id(); - - int64_t bucketStartTimeNs = 10000000000; - - int configUid = 2000; - int64_t configId = 1000; - ConfigKey cfgKey(configUid, configId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_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(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(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - metadata::StatsMetadataList result; - int64_t mockWallClockNs = 1584991200 * NS_PER_SEC; - int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC; - processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result); - - metadata::StatsMetadata statsMetadata = result.stats_metadata(0); - ASSERT_EQ(result.stats_metadata_size(), 1); - EXPECT_EQ(statsMetadata.config_key().config_id(), configId); - EXPECT_EQ(statsMetadata.config_key().uid(), configUid); - - metadata::AlertMetadata alertMetadata = statsMetadata.alert_metadata(0); - ASSERT_EQ(statsMetadata.alert_metadata_size(), 1); - EXPECT_EQ(alertMetadata.alert_id(), alert_id); - metadata::AlertDimensionKeyedData keyedData = alertMetadata.alert_dim_keyed_data(0); - ASSERT_EQ(alertMetadata.alert_dim_keyed_data_size(), 1); - EXPECT_EQ(keyedData.last_refractory_ends_sec(), - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) - - mockElapsedTimeNs / NS_PER_SEC + - mockWallClockNs / NS_PER_SEC); - - metadata::MetricDimensionKey metadataDimKey = keyedData.dimension_key(); - metadata::FieldValue dimKeyInWhat = metadataDimKey.dimension_key_in_what(0); - EXPECT_EQ(dimKeyInWhat.field().tag(), fieldValue1.mField.getTag()); - EXPECT_EQ(dimKeyInWhat.field().field(), fieldValue1.mField.getField()); - EXPECT_EQ(dimKeyInWhat.value_int(), fieldValue1.mValue.int_value); -} - -TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk) { - const int num_buckets = 1; - const int threshold = 0; - const int refractory_period_sec = 86400 * 365; // 1 year - auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); - const int64_t alert_id = config.alert(0).id(); - - int64_t bucketStartTimeNs = 10000000000; - - int configUid = 2000; - int64_t configId = 1000; - ConfigKey cfgKey(configUid, configId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_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(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(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - int64_t mockWallClockNs = 1584991200 * NS_PER_SEC; - int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC; - processor->SaveMetadataToDisk(mockWallClockNs, mockElapsedTimeNs); - - auto processor2 = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - int64_t mockElapsedTimeSinceBoot = 10 * NS_PER_SEC; - processor2->LoadMetadataFromDisk(mockWallClockNs, mockElapsedTimeSinceBoot); - - sp<AnomalyTracker> anomalyTracker2 = - processor2->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; - EXPECT_EQ(anomalyTracker2->getRefractoryPeriodEndsSec(dimensionKey1) - - mockElapsedTimeSinceBoot / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) - - mockElapsedTimeNs / NS_PER_SEC); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp deleted file mode 100644 index 70e7365ec238..000000000000 --- a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp +++ /dev/null @@ -1,527 +0,0 @@ -// 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. - -#include <android/binder_ibinder.h> -#include <android/binder_interface_utils.h> -#include <gtest/gtest.h> - -#include <vector> - -#include "src/StatsLogProcessor.h" -#include "src/StatsService.h" -#include "src/anomaly/DurationAnomalyTracker.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -using ::ndk::SharedRefBase; - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -const int kConfigKey = 789130124; -const int kCallingUid = 0; - -StatsdConfig CreateStatsdConfig(int num_buckets, - uint64_t threshold_ns, - DurationMetric::AggregationType aggregationType, - bool nesting) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = screenIsOffPredicate; - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - FieldMatcher dimensions = CreateAttributionUidDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock. - *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; - holdingWakelockPredicate.mutable_simple_predicate()->set_count_nesting(nesting); - *config.add_predicate() = holdingWakelockPredicate; - - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("WakelockDuration")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->set_condition(screenIsOffPredicate.id()); - durationMetric->set_aggregation_type(aggregationType); - *durationMetric->mutable_dimensions_in_what() = - CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - durationMetric->set_bucket(FIVE_MINUTES); - - auto alert = config.add_alert(); - alert->set_id(StringToId("alert")); - alert->set_metric_id(StringToId("WakelockDuration")); - alert->set_num_buckets(num_buckets); - alert->set_refractory_period_secs(2); - alert->set_trigger_if_sum_gt(threshold_ns); - return config; -} - -std::vector<int> attributionUids1 = {111, 222}; -std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1"}; - -std::vector<int> attributionUids2 = {111, 222}; -std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1"}; - -std::vector<int> attributionUids3 = {222}; -std::vector<string> attributionTags3 = {"GMSCoreModule1"}; - -MetricDimensionKey dimensionKey1( - HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED, - (int32_t)0x02010101), - Value((int32_t)111))}), - DEFAULT_DIMENSION_KEY); - -MetricDimensionKey dimensionKey2( - HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED, - (int32_t)0x02010101), Value((int32_t)222))}), - DEFAULT_DIMENSION_KEY); - -void sendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) { - string str; - config.SerializeToString(&str); - std::vector<uint8_t> configAsVec(str.begin(), str.end()); - service->addConfiguration(kConfigKey, configAsVec, kCallingUid); -} - -} // 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(); - - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - sendConfig(service, config); - - auto processor = service->mProcessor; - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - int64_t bucketStartTimeNs = processor->mTimeBaseNs; - int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6; - - 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); - EXPECT_EQ(alarmFiredTimestampSec0, - processor->getAnomalyAlarmMonitor()->getRegisteredAlarmTimeSec()); - - // Anomaly alarm fired. - auto alarmTriggerEvent = CreateBatterySaverOnEvent(alarmFiredTimestampSec0 * NS_PER_SEC); - processor->OnLogEvent(alarmTriggerEvent.get(), alarmFiredTimestampSec0 * NS_PER_SEC); - - 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( - roundedBucketStartTimeNs + 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. - int64_t release_event_time = roundedBucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( - static_cast<uint32_t>(alarmFiredTimestampSec1)); - ASSERT_EQ(0u, alarmSet.size()); - - // Acquire wakelock wl1 near the end of bucket #0. - acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 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_time = roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - 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(roundedBucketStartTimeNs + 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. - int64_t condition_false_time = bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1; - screen_on_event = CreateScreenStateChangedEvent( - condition_false_time, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - processor->OnLogEvent(screen_on_event.get(), condition_false_time); - // 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_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(release_event.get()); - EXPECT_EQ(refractory_period_sec + (release_event_time) / 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(); - - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - sendConfig(service, config); - - auto processor = service->mProcessor; - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - int64_t bucketStartTimeNs = processor->mTimeBaseNs; - int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6; - - 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(roundedBucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Release wakelock "wc1" in bucket #0. - int64_t release_event_time = roundedBucketStartTimeNs + bucketSizeNs - 1; - auto release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Acquire wakelock "wc1" in bucket #1. - acquire_event = - CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC + 1, - attributionUids2, attributionTags2, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - release_event_time = roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC + 100; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Acquire wakelock "wc2" in bucket #2. - acquire_event = - CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + 1, - attributionUids3, attributionTags3, "wl2"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3, - anomalyTracker->getAlarmTimestampSec(dimensionKey2)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); - - // Release wakelock "wc2" in bucket #2. - release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids3, - attributionTags3, "wl2"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); - EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); - - // Acquire wakelock "wc1" in bucket #2. - acquire_event = - CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, - attributionUids2, attributionTags2, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3 + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Release wakelock "wc1" in bucket #2. - release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 3.5 * NS_PER_SEC; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 6 * bucketSizeNs + 4, - attributionUids3, attributionTags3, "wl2"); - processor->OnLogEvent(acquire_event.get()); - acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 6 * bucketSizeNs + 5, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 2, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ((roundedBucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 2, - anomalyTracker->getAlarmTimestampSec(dimensionKey2)); - - release_event_time = roundedBucketStartTimeNs + 6 * bucketSizeNs + NS_PER_SEC + 2; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids3, - attributionTags3, "wl2"); - processor->OnLogEvent(release_event.get(), release_event_time); - release_event = CreateReleaseWakelockEvent(release_event_time + 4, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time + 4); - 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)(roundedBucketStartTimeNs + 6 * bucketSizeNs + NS_PER_SEC) / - 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); - const uint64_t alert_id = config.alert(0).id(); - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6; - const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC; - config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec); - - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - sendConfig(service, config); - - auto processor = service->mProcessor; - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - int64_t bucketStartTimeNs = processor->mTimeBaseNs; - int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC; - - 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(roundedBucketStartTimeNs + bucketSizeNs - 100, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Acquire the wakelock "wc1" again. - acquire_event = - CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - // The alarm does not change. - EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Anomaly alarm fired late. - const int64_t firedAlarmTimestampNs = roundedBucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC; - auto alarmTriggerEvent = CreateBatterySaverOnEvent(firedAlarmTimestampNs); - processor->OnLogEvent(alarmTriggerEvent.get(), firedAlarmTimestampNs); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 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)); - - int64_t release_event_time = bucketStartTimeNs + 2 * bucketSizeNs + 1; - auto release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - 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( - roundedBucketStartTimeNs + 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(roundedBucketStartTimeNs + 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( - roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 5, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - - release_event_time = roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 4; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - - acquire_event = CreateAcquireWakelockEvent( - roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 3, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp deleted file mode 100644 index 4c2caa904f6a..000000000000 --- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp +++ /dev/null @@ -1,375 +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. - -#include <gtest/gtest.h> - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include <iostream> -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig(const Position position) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - auto attributionNodeMatcher = - wakelockAcquireMatcher.mutable_simple_atom_matcher()->add_field_value_matcher(); - attributionNodeMatcher->set_field(1); - attributionNodeMatcher->set_position(Position::ANY); - auto uidMatcher = attributionNodeMatcher->mutable_matches_tuple()->add_field_value_matcher(); - uidMatcher->set_field(1); // uid field. - uidMatcher->set_eq_string("com.android.gmscore"); - - *config.add_atom_matcher() = wakelockAcquireMatcher; - - auto countMetric = config.add_count_metric(); - countMetric->set_id(123456); - countMetric->set_what(wakelockAcquireMatcher.id()); - *countMetric->mutable_dimensions_in_what() = - CreateAttributionUidAndTagDimensions( - util::WAKELOCK_STATE_CHANGED, {position}); - countMetric->set_bucket(FIVE_MINUTES); - 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 - -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); - ASSERT_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); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(countMetrics.data_size(), 4); - - auto data = countMetrics.data(0); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 111, "App1"); - ASSERT_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(), - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ASSERT_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(), - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule3"); - ASSERT_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(), - util::WAKELOCK_STATE_CHANGED, 444, - "GMSCoreModule2"); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(countMetrics.data_size(), 6); - - auto data = countMetrics.data(0); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ASSERT_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, util::WAKELOCK_STATE_CHANGED, 222); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 333); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, - util::WAKELOCK_STATE_CHANGED, 333, "App3"); - ASSERT_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, util::WAKELOCK_STATE_CHANGED, 444); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, - util::WAKELOCK_STATE_CHANGED, 444, - "GMSCoreModule2"); - ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 222); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ASSERT_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, util::WAKELOCK_STATE_CHANGED, 111); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, - util::WAKELOCK_STATE_CHANGED, 111, "App1"); - ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 222); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 333); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, - util::WAKELOCK_STATE_CHANGED, 333, "App3"); - ASSERT_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, util::WAKELOCK_STATE_CHANGED, 111); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, - util::WAKELOCK_STATE_CHANGED, 111, "App1"); - ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 333); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, - util::WAKELOCK_STATE_CHANGED, 333, "App3"); - ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 222); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ASSERT_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, util::WAKELOCK_STATE_CHANGED, 111); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, - util::WAKELOCK_STATE_CHANGED, 111, "App1"); - ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 444); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, - util::WAKELOCK_STATE_CHANGED, 444, - "GMSCoreModule2"); - ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 333); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, - util::WAKELOCK_STATE_CHANGED, 333, "App3"); - ASSERT_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"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp deleted file mode 100644 index 0bce0baa049b..000000000000 --- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// 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. - -#include <gtest/gtest.h> - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - - *config.add_atom_matcher() = wakelockAcquireMatcher; - - auto countMetric = config.add_count_metric(); - countMetric->set_id(123456); - countMetric->set_what(wakelockAcquireMatcher.id()); - *countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - countMetric->set_bucket(FIVE_MINUTES); - - auto alert = config.add_alert(); - alert->set_id(StringToId("alert")); - alert->set_metric_id(123456); - alert->set_num_buckets(num_buckets); - alert->set_refractory_period_secs(10); - alert->set_trigger_if_sum_gt(threshold); - - // Two hours - config.set_ttl_in_seconds(2 * 3600); - return config; -} - -} // namespace - -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); - ASSERT_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(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), - Value((int32_t)111)); - HashableDimensionKey whatKey1({fieldValue1}); - MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); - - FieldValue fieldValue2(Field(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"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp deleted file mode 100644 index e01a0b63a0ca..000000000000 --- a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp +++ /dev/null @@ -1,307 +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. - -#include <gtest/gtest.h> - -#include <thread> // std::this_thread::sleep_for - -#include "android-base/stringprintf.h" -#include "src/StatsLogProcessor.h" -#include "src/storage/StorageManager.h" -#include "tests/statsd_test_util.h" - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ -#define STATS_DATA_DIR "/data/misc/stats-data" -using android::base::StringPrintf; - -namespace { - -StatsdConfig CreateSimpleConfig() { - StatsdConfig config; - config.add_allowed_log_source("AID_STATSD"); - config.set_hash_strings_in_metric_report(false); - - *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); - // Simple count metric so the config isn't empty. - CountMetric* countMetric1 = config.add_count_metric(); - countMetric1->set_id(StringToId("Count1")); - countMetric1->set_what(config.atom_matcher(0).id()); - countMetric1->set_bucket(FIVE_MINUTES); - return config; -} -} // namespace - -// Setup for parameterized tests. -class ConfigUpdateE2eTest : public TestWithParam<bool> {}; - -INSTANTIATE_TEST_SUITE_P(ConfigUpdateE2eTest, ConfigUpdateE2eTest, testing::Bool()); - -TEST_P(ConfigUpdateE2eTest, TestUidMapVersionStringInstaller) { - sp<UidMap> uidMap = new UidMap(); - vector<int32_t> uids({1000}); - vector<int64_t> versions({1}); - vector<String16> apps({String16("app1")}); - vector<String16> versionStrings({String16("v1")}); - vector<String16> installers({String16("installer1")}); - uidMap->updateMap(1, uids, versions, versionStrings, apps, installers); - - StatsdConfig config = CreateSimpleConfig(); - config.set_version_strings_in_metric_report(true); - config.set_installer_in_metric_report(false); - int64_t baseTimeNs = getElapsedRealtimeNs(); - - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - - // Now update. - config.set_version_strings_in_metric_report(false); - config.set_installer_in_metric_report(true); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam()); - EXPECT_TRUE(metricsManager->isConfigValid()); - - ConfigMetricsReportList reports; - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - // First report is written to disk when the update happens. - ASSERT_EQ(reports.reports_size(), 2); - UidMapping uidMapping = reports.reports(1).uid_map(); - ASSERT_EQ(uidMapping.snapshots_size(), 1); - ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1); - EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string()); - EXPECT_EQ(uidMapping.snapshots(0).package_info(0).installer(), "installer1"); -} - -TEST_P(ConfigUpdateE2eTest, TestHashStrings) { - sp<UidMap> uidMap = new UidMap(); - vector<int32_t> uids({1000}); - vector<int64_t> versions({1}); - vector<String16> apps({String16("app1")}); - vector<String16> versionStrings({String16("v1")}); - vector<String16> installers({String16("installer1")}); - uidMap->updateMap(1, uids, versions, versionStrings, apps, installers); - - StatsdConfig config = CreateSimpleConfig(); - config.set_version_strings_in_metric_report(true); - config.set_hash_strings_in_metric_report(true); - int64_t baseTimeNs = getElapsedRealtimeNs(); - - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - - // Now update. - config.set_hash_strings_in_metric_report(false); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam()); - EXPECT_TRUE(metricsManager->isConfigValid()); - - ConfigMetricsReportList reports; - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - // First report is written to disk when the update happens. - ASSERT_EQ(reports.reports_size(), 2); - UidMapping uidMapping = reports.reports(1).uid_map(); - ASSERT_EQ(uidMapping.snapshots_size(), 1); - ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1); - EXPECT_TRUE(uidMapping.snapshots(0).package_info(0).has_version_string()); - EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string_hash()); -} - -TEST_P(ConfigUpdateE2eTest, TestAnnotations) { - StatsdConfig config = CreateSimpleConfig(); - StatsdConfig_Annotation* annotation = config.add_annotation(); - annotation->set_field_int64(11); - annotation->set_field_int32(1); - int64_t baseTimeNs = getElapsedRealtimeNs(); - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); - - // Now update - config.clear_annotation(); - annotation = config.add_annotation(); - annotation->set_field_int64(22); - annotation->set_field_int32(2); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); - - ConfigMetricsReportList reports; - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - // First report is written to disk when the update happens. - ASSERT_EQ(reports.reports_size(), 2); - ConfigMetricsReport report = reports.reports(1); - EXPECT_EQ(report.annotation_size(), 1); - EXPECT_EQ(report.annotation(0).field_int64(), 22); - EXPECT_EQ(report.annotation(0).field_int32(), 2); -} - -TEST_P(ConfigUpdateE2eTest, TestPersistLocally) { - StatsdConfig config = CreateSimpleConfig(); - config.set_persist_locally(false); - int64_t baseTimeNs = getElapsedRealtimeNs(); - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); - ConfigMetricsReportList reports; - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 1); - // Number of reports should still be 1 since persist_locally is false. - reports.Clear(); - buffer.clear(); - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 1); - - // Now update. - config.set_persist_locally(true); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); - - // Should get 2: 1 in memory + 1 on disk. Both should be saved on disk. - reports.Clear(); - buffer.clear(); - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 2); - // Should get 3, 2 on disk + 1 in memory. - reports.Clear(); - buffer.clear(); - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 3); - string suffix = StringPrintf("%d_%lld", cfgKey.GetUid(), (long long)cfgKey.GetId()); - StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, suffix.c_str()); - string historySuffix = - StringPrintf("%d_%lld_history", cfgKey.GetUid(), (long long)cfgKey.GetId()); - StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, historySuffix.c_str()); -} - -TEST_P(ConfigUpdateE2eTest, TestNoReportMetrics) { - StatsdConfig config = CreateSimpleConfig(); - // Second simple count metric. - CountMetric* countMetric = config.add_count_metric(); - countMetric->set_id(StringToId("Count2")); - countMetric->set_what(config.atom_matcher(0).id()); - countMetric->set_bucket(FIVE_MINUTES); - config.add_no_report_metric(config.count_metric(0).id()); - int64_t baseTimeNs = getElapsedRealtimeNs(); - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); - - // Now update. - config.clear_no_report_metric(); - config.add_no_report_metric(config.count_metric(1).id()); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); - - ConfigMetricsReportList reports; - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - // First report is written to disk when the update happens. - ASSERT_EQ(reports.reports_size(), 2); - // First report (before update) has the first count metric. - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - EXPECT_EQ(reports.reports(0).metrics(0).metric_id(), config.count_metric(1).id()); - // Second report (after update) has the first count metric. - ASSERT_EQ(reports.reports(1).metrics_size(), 1); - EXPECT_EQ(reports.reports(1).metrics(0).metric_id(), config.count_metric(0).id()); -} - -TEST_P(ConfigUpdateE2eTest, TestAtomsAllowedFromAnyUid) { - StatsdConfig config = CreateSimpleConfig(); - int64_t baseTimeNs = getElapsedRealtimeNs(); - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); - // Uses AID_ROOT, which isn't in allowed log sources. - unique_ptr<LogEvent> event = CreateBatteryStateChangedEvent( - baseTimeNs + 2, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); - processor->OnLogEvent(event.get()); - ConfigMetricsReportList reports; - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, true, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 1); - // Check the metric and make sure it has 0 count. - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - EXPECT_FALSE(reports.reports(0).metrics(0).has_count_metrics()); - - // Now update. Allow plugged state to be logged from any uid, so the atom will be counted. - config.add_whitelisted_atom_ids(util::PLUGGED_STATE_CHANGED); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); - unique_ptr<LogEvent> event2 = CreateBatteryStateChangedEvent( - baseTimeNs + 2000, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); - processor->OnLogEvent(event.get()); - reports.Clear(); - buffer.clear(); - processor->onDumpReport(cfgKey, baseTimeNs + 3000, true, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 2); - // Check the metric and make sure it has 0 count. - ASSERT_EQ(reports.reports(1).metrics_size(), 1); - EXPECT_TRUE(reports.reports(1).metrics(0).has_count_metrics()); - ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data_size(), 1); - ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info_size(), 1); - EXPECT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); -} - -TEST_P(ConfigUpdateE2eTest, TestConfigTtl) { - StatsdConfig config = CreateSimpleConfig(); - config.set_ttl_in_seconds(1); - int64_t baseTimeNs = getElapsedRealtimeNs(); - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + NS_PER_SEC); - - config.set_ttl_in_seconds(5); - processor->OnConfigUpdated(baseTimeNs + 2 * NS_PER_SEC, cfgKey, config, - /*modularUpdate=*/GetParam()); - metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + 7 * NS_PER_SEC); - - // Clear the data stored on disk as a result of the update. - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 3 * NS_PER_SEC, false, true, ADB_DUMP, FAST, - &buffer); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp deleted file mode 100644 index 04eb40080631..000000000000 --- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp +++ /dev/null @@ -1,901 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <gtest/gtest.h> - -#include "src/StatsLogProcessor.h" -#include "src/state/StateManager.h" -#include "src/state/StateTracker.h" -#include "tests/statsd_test_util.h" - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -/** - * Tests the initial condition and condition after the first log events for - * count metrics with either a combination condition or simple condition. - * - * Metrics should be initialized with condition kUnknown (given that the - * predicate is using the default InitialValue of UNKNOWN). The condition should - * be updated to either kFalse or kTrue if a condition event is logged for all - * children conditions. - */ -TEST(CountMetricE2eTest, TestInitialConditionChanges) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - - auto syncStartMatcher = CreateSyncStartAtomMatcher(); - *config.add_atom_matcher() = syncStartMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = CreateBatteryStateNoneMatcher(); - *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); - - auto screenOnPredicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = screenOnPredicate; - - auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate(); - *config.add_predicate() = deviceUnpluggedPredicate; - - auto screenOnOnBatteryPredicate = config.add_predicate(); - screenOnOnBatteryPredicate->set_id(StringToId("screenOnOnBatteryPredicate")); - screenOnOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenOnPredicate, screenOnOnBatteryPredicate); - addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOnOnBatteryPredicate); - - // CountSyncStartWhileScreenOnOnBattery (CombinationCondition) - CountMetric* countMetric1 = config.add_count_metric(); - countMetric1->set_id(StringToId("CountSyncStartWhileScreenOnOnBattery")); - countMetric1->set_what(syncStartMatcher.id()); - countMetric1->set_condition(screenOnOnBatteryPredicate->id()); - countMetric1->set_bucket(FIVE_MINUTES); - - // CountSyncStartWhileOnBattery (SimpleCondition) - CountMetric* countMetric2 = config.add_count_metric(); - countMetric2->set_id(StringToId("CountSyncStartWhileOnBatterySliceScreen")); - countMetric2->set_what(syncStartMatcher.id()); - countMetric2->set_condition(deviceUnpluggedPredicate.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - 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); - - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - EXPECT_EQ(2, metricsManager->mAllMetricProducers.size()); - - sp<MetricProducer> metricProducer1 = metricsManager->mAllMetricProducers[0]; - sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1]; - - EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); - - auto screenOnEvent = - CreateScreenStateChangedEvent(bucketStartTimeNs + 30, android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); - - auto pluggedUsbEvent = CreateBatteryStateChangedEvent( - bucketStartTimeNs + 50, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); - processor->OnLogEvent(pluggedUsbEvent.get()); - EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kFalse, metricProducer2->mCondition); - - auto pluggedNoneEvent = CreateBatteryStateChangedEvent( - bucketStartTimeNs + 70, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE); - processor->OnLogEvent(pluggedNoneEvent.get()); - EXPECT_EQ(ConditionState::kTrue, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition); -} - -/** -* 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. - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - ASSERT_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) - | (ScreenDozeEvent) - */ - // 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); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(3, countMetrics.data_size()); - - // For each CountMetricData, check StateValue info is correct and buckets - // have correct counts. - auto data = countMetrics.data(0); - ASSERT_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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(1); - ASSERT_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()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(2, data.bucket_info(1).count()); - - data = countMetrics.data(2); - ASSERT_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()); - ASSERT_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 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; - - int64_t screenOnId = 4444; - int64_t screenOffId = 9876; - auto state = CreateScreenStateWithOnOffMap(screenOnId, screenOffId); - *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. - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1); - - StateMap map = state.map(); - for (auto group : map.group()) { - for (auto value : group.value()) { - EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(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 - | | (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_DOZE)); // 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); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(3, countMetrics.data_size()); - - // For each CountMetricData, check StateValue info is correct and buckets - // have correct counts. - auto data = countMetrics.data(0); - ASSERT_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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(1); - ASSERT_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(screenOnId, data.slice_by_state(0).group_id()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(1, data.bucket_info(1).count()); - - data = countMetrics.data(2); - ASSERT_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(screenOffId, data.slice_by_state(0).group_id()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(4, 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 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", 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(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. - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0); - ASSERT_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); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(5, countMetrics.data_size()); - - // For each CountMetricData, check StateValue info is correct and buckets - // have correct counts. - auto data = countMetrics.data(0); - ASSERT_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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(1); - ASSERT_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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); - - data = countMetrics.data(2); - ASSERT_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()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(2, data.bucket_info(1).count()); - - data = countMetrics.data(3); - ASSERT_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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); - - data = countMetrics.data(4); - ASSERT_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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).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", util::APP_CRASH_OCCURRED); - *config.add_atom_matcher() = appCrashMatcher; - - int64_t screenOnId = 4444; - int64_t screenOffId = 9876; - auto state1 = CreateScreenStateWithOnOffMap(screenOnId, screenOffId); - *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(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. - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_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); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1); - ASSERT_EQ(metricProducer->mMetric2StateLinks.size(), 1); - - StateMap map = state1.map(); - for (auto group : map.group()) { - for (auto value : group.value()) { - EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(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 - | | (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_DOZE)); // 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); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(6, countMetrics.data_size()); - - // For each CountMetricData, check StateValue info is correct and buckets - // have correct counts. - auto data = countMetrics.data(0); - ASSERT_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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(1); - ASSERT_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(screenOnId, 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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(2); - ASSERT_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(screenOnId, 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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(3); - ASSERT_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(screenOffId, 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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); - - data = countMetrics.data(4); - ASSERT_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(screenOffId, 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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(5); - ASSERT_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(screenOffId, 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()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); - EXPECT_EQ(1, data.bucket_info(1).count()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp deleted file mode 100644 index 4efb038e538d..000000000000 --- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp +++ /dev/null @@ -1,1473 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <gtest/gtest.h> - -#include <vector> - -#include "src/StatsLogProcessor.h" -#include "src/state/StateTracker.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -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); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_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()); - - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_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); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_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()); - - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_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 - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_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()); - - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_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); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_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); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - - // Validate bucket info. - ASSERT_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(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(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( - 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(util::WAKELOCK_STATE_CHANGED); - dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - 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); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_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); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - // Validate dimension value. - ValidateAttributionUidDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, appUid); - // Validate bucket info. - ASSERT_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(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(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( - 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(util::WAKELOCK_STATE_CHANGED); - dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - 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); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - ASSERT_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); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - // Validate dimension value. - ValidateAttributionUidDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, appUid); - // Validate bucket info. - ASSERT_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, TestWithSlicedState) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher(); - *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher(); - - auto batterySaverModePredicate = CreateBatterySaverModePredicate(); - *config.add_predicate() = batterySaverModePredicate; - - auto screenState = CreateScreenState(); - *config.add_state() = screenState; - - // Create duration metric that slices by screen state. - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("DurationBatterySaverModeSliceScreen")); - durationMetric->set_what(batterySaverModePredicate.id()); - durationMetric->add_slice_by_state(screenState.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - // Initialize StatsLogProcessor. - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - uint64_t bucketStartTimeNs = 10000000000; // 0:10 - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - EXPECT_TRUE(metricsManager->isActive()); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - EXPECT_TRUE(metricProducer->mIsActive); - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - ASSERT_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) - |-----------------------------|-----------------------------|-- - ON OFF ON (BatterySaverMode) - | | | (ScreenIsOnEvent) - | | (ScreenIsOffEvent) - | (ScreenDozeEvent) - */ - // Initialize log events. - std::vector<std::unique_ptr<LogEvent>> events; - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 10 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:20 - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 50 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:00 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 80 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 1:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 120 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:10 - events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 250 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:20 - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 - - // Bucket boundary. - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 310 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 5:20 - - // 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 + 360 * NS_PER_SEC, - true /* include current partial bucket */, true, ADB_DUMP, FAST, - &buffer); // 6:10 - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(3, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_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()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(370 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(1); - ASSERT_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()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(110 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(370 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(2); - ASSERT_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_DOZE, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher(); - *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher(); - *config.add_atom_matcher() = CreateBatteryStateNoneMatcher(); - *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); - - auto batterySaverModePredicate = CreateBatterySaverModePredicate(); - *config.add_predicate() = batterySaverModePredicate; - - auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate(); - *config.add_predicate() = deviceUnpluggedPredicate; - - auto screenState = CreateScreenState(); - *config.add_state() = screenState; - - // Create duration metric that has a condition and slices by screen state. - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("DurationBatterySaverModeOnBatterySliceScreen")); - durationMetric->set_what(batterySaverModePredicate.id()); - durationMetric->set_condition(deviceUnpluggedPredicate.id()); - durationMetric->add_slice_by_state(screenState.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - // Initialize StatsLogProcessor. - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - uint64_t bucketStartTimeNs = 10000000000; // 0:10 - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - EXPECT_TRUE(metricsManager->isActive()); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - EXPECT_TRUE(metricProducer->mIsActive); - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - ASSERT_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 (minutes) - |---------------------------------------|------------------ - ON OFF ON (BatterySaverMode) - T F T (DeviceUnpluggedPredicate) - | | | (ScreenIsOnEvent) - | | | (ScreenIsOffEvent) - | (ScreenDozeEvent) - */ - // Initialize log events. - std::vector<std::unique_ptr<LogEvent>> events; - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 20 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:30 - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 80 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:30 - events.push_back( - CreateBatteryStateChangedEvent(bucketStartTimeNs + 110 * NS_PER_SEC, - BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // 2:00 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 145 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:35 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 170 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 3:00 - events.push_back( - CreateBatteryStateChangedEvent(bucketStartTimeNs + 180 * NS_PER_SEC, - BatteryPluggedStateEnum::BATTERY_PLUGGED_USB)); // 3:10 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 200 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:30 - events.push_back( - CreateBatteryStateChangedEvent(bucketStartTimeNs + 230 * NS_PER_SEC, - BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // 4:00 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 260 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 4:30 - events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 - - // Bucket boundary. - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 320 * NS_PER_SEC)); // 5:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 380 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 6:30 - - // 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 + 410 * NS_PER_SEC, - true /* include current partial bucket */, true, ADB_DUMP, FAST, - &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(3, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_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()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(45 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(420 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(1); - ASSERT_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()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(45 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(60 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(420 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(2); - ASSERT_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_DOZE, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -TEST(DurationMetricE2eTest, TestWithSlicedStateMapped) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher(); - *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher(); - - auto batterySaverModePredicate = CreateBatterySaverModePredicate(); - *config.add_predicate() = batterySaverModePredicate; - - int64_t screenOnId = 4444; - int64_t screenOffId = 9876; - auto screenStateWithMap = CreateScreenStateWithOnOffMap(screenOnId, screenOffId); - *config.add_state() = screenStateWithMap; - - // Create duration metric that slices by mapped screen state. - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("DurationBatterySaverModeSliceScreenMapped")); - durationMetric->set_what(batterySaverModePredicate.id()); - durationMetric->add_slice_by_state(screenStateWithMap.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - // Initialize StatsLogProcessor. - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - uint64_t bucketStartTimeNs = 10000000000; // 0:10 - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - EXPECT_TRUE(metricsManager->isActive()); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - EXPECT_TRUE(metricProducer->mIsActive); - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1); - - // 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) - |-----------------------------|-----------------------------|-- - ON OFF ON (BatterySaverMode) - ---------------------------------------------------------SCREEN_OFF events - | | (ScreenStateOffEvent = 1) - | (ScreenStateDozeEvent = 3) - | (ScreenStateDozeSuspendEvent = 4) - ---------------------------------------------------------SCREEN_ON events - | | | (ScreenStateOnEvent = 2) - | (ScreenStateVrEvent = 5) - | (ScreenStateOnSuspendEvent = 6) - */ - // Initialize log events. - std::vector<std::unique_ptr<LogEvent>> events; - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 10 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:20 - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 70 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:20 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 100 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 1:50 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 120 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:10 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 170 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_VR)); // 3:00 - events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 250 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:20 - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 - - // Bucket boundary 5:10. - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 320 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 5:30 - 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 - // 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 + 490 * NS_PER_SEC, - true /* include current partial bucket */, true, ADB_DUMP, FAST, - &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(2, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_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(screenOnId, data.slice_by_state(0).group_id()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(130 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(110 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(500 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(1); - ASSERT_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(screenOffId, data.slice_by_state(0).group_id()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(70 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(80 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(500 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); -} - -TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat) { - // Initialize config. - 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(); - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - *config.add_predicate() = holdingWakelockPredicate; - - auto uidProcessState = CreateUidProcessState(); - *config.add_state() = uidProcessState; - - // Create duration metric that slices by uid process state. - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("DurationHoldingWakelockSliceUidProcessState")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->add_slice_by_state(uidProcessState.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - // The state has only one primary field (uid). - auto stateLink = durationMetric->add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - - // Initialize StatsLogProcessor. - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - uint64_t bucketStartTimeNs = 10000000000; // 0:10 - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - // This config is rejected because the dimension in what fields are not a superset of the sliced - // state primary fields. - ASSERT_EQ(processor->mMetricsManagers.size(), 0); -} - -TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset) { - // Initialize config. - 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(); - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - *config.add_predicate() = holdingWakelockPredicate; - - auto uidProcessState = CreateUidProcessState(); - *config.add_state() = uidProcessState; - - // Create duration metric that slices by uid process state. - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("DurationPartialWakelockPerTagUidSliceProcessState")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->add_slice_by_state(uidProcessState.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - // The metric is dimensioning by first uid of attribution node and tag. - *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidAndOtherDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}, {3 /* tag */}); - // The state has only one primary field (uid). - auto stateLink = durationMetric->add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - - // Initialize StatsLogProcessor. - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - uint64_t bucketStartTimeNs = 10000000000; // 0:10 - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - EXPECT_TRUE(metricsManager->isActive()); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - EXPECT_TRUE(metricProducer->mIsActive); - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0); - - // Check that StateTrackers were initialized correctly. - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); - - // Initialize log events. - int appUid1 = 1001; - int appUid2 = 1002; - std::vector<int> attributionUids1 = {appUid1}; - std::vector<string> attributionTags1 = {"App1"}; - - std::vector<int> attributionUids2 = {appUid2}; - std::vector<string> attributionTags2 = {"App2"}; - - std::vector<std::unique_ptr<LogEvent>> events; - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 10 * NS_PER_SEC, appUid1, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:20 - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 20 * NS_PER_SEC, - attributionUids1, attributionTags1, - "wakelock1")); // 0:30 - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * NS_PER_SEC, - attributionUids1, attributionTags1, - "wakelock2")); // 0:35 - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 30 * NS_PER_SEC, - attributionUids2, attributionTags2, - "wakelock1")); // 0:40 - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC, - attributionUids2, attributionTags2, - "wakelock2")); // 0:45 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 50 * NS_PER_SEC, appUid2, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 1:00 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 60 * NS_PER_SEC, appUid1, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 1:10 - events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 100 * NS_PER_SEC, - attributionUids2, attributionTags2, - "wakelock1")); // 1:50 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 120 * NS_PER_SEC, appUid2, - android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 2:10 - events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 200 * NS_PER_SEC, - attributionUids1, attributionTags1, - "wakelock2")); // 3:30 - - // 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 + 320 * NS_PER_SEC, - true /* include current partial bucket */, true, ADB_DUMP, FAST, - &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(9, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1, - "wakelock1"); - ASSERT_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::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(1); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1, - "wakelock1"); - ASSERT_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::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(240 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(330 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(2); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1, - "wakelock2"); - ASSERT_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::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(3); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1, - "wakelock2"); - ASSERT_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::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(140 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(4); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2, - "wakelock1"); - ASSERT_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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(5); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2, - "wakelock1"); - ASSERT_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::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(6); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2, - "wakelock2"); - ASSERT_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()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(15 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(7); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2, - "wakelock2"); - ASSERT_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::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, - data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(180 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(330 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(8); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2, - "wakelock2"); - ASSERT_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::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(70 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp deleted file mode 100644 index 1be261297e0a..000000000000 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp +++ /dev/null @@ -1,624 +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. - -#include <android/binder_interface_utils.h> -#include <gtest/gtest.h> - -#include <vector> - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -using ::ndk::SharedRefBase; - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -const int64_t metricId = 123456; -const int32_t ATOM_TAG = util::SUBSYSTEM_SLEEP_STATE; - -StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type, - bool useCondition = true) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", ATOM_TAG); - *config.add_atom_matcher() = atomMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = screenIsOffPredicate; - - auto gaugeMetric = config.add_gauge_metric(); - gaugeMetric->set_id(metricId); - gaugeMetric->set_what(atomMatcher.id()); - if (useCondition) { - gaugeMetric->set_condition(screenIsOffPredicate.id()); - } - gaugeMetric->set_sampling_type(sampling_type); - gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true); - *gaugeMetric->mutable_dimensions_in_what() = - CreateDimensions(ATOM_TAG, {1 /* subsystem name */}); - gaugeMetric->set_bucket(FIVE_MINUTES); - gaugeMetric->set_max_pull_delay_sec(INT_MAX); - config.set_hash_strings_in_metric_report(false); - - return config; -} - -} // namespaces - -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); - ASSERT_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. - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); - ASSERT_GT((int)gaugeMetrics.data_size(), 1); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_EQ(6, data.bucket_info_size()); - - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); - ASSERT_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); - - ASSERT_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); - - ASSERT_EQ(1, data.bucket_info(2).atom_size()); - ASSERT_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); - - ASSERT_EQ(1, data.bucket_info(3).atom_size()); - ASSERT_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); - - ASSERT_EQ(1, data.bucket_info(4).atom_size()); - ASSERT_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); - - ASSERT_EQ(1, data.bucket_info(5).atom_size()); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); - ASSERT_GT((int)gaugeMetrics.data_size(), 1); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_EQ(3, data.bucket_info_size()); - - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); - ASSERT_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); - - ASSERT_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); - - ASSERT_EQ(2, data.bucket_info(2).atom_size()); - ASSERT_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); - ASSERT_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. - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); - ASSERT_GT((int)gaugeMetrics.data_size(), 1); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_EQ(3, data.bucket_info_size()); - - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_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); - - ASSERT_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); - - ASSERT_EQ(1, data.bucket_info(2).atom_size()); - ASSERT_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); - ASSERT_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. - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); - ASSERT_GT((int)gaugeMetrics.data_size(), 0); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_EQ(3, data.bucket_info_size()); - - auto bucketInfo = data.bucket_info(0); - ASSERT_EQ(1, bucketInfo.atom_size()); - ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); - EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0)); - ASSERT_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); - ASSERT_EQ(1, bucketInfo.atom_size()); - ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0)); - ASSERT_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); - ASSERT_EQ(1, bucketInfo.atom_size()); - ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0)); - ASSERT_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); - - 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); - ASSERT_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. - ASSERT_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. - processor->informPullAlarmFired(nextPullTimeNs + 1); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); - - processor->informPullAlarmFired(nextPullTimeNs + 4); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * 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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue( - reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); - ASSERT_GT((int)gaugeMetrics.data_size(), 0); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_EQ(3, data.bucket_info_size()); - - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).elapsed_timestamp_nanos(0)); - ASSERT_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); - - ASSERT_EQ(1, data.bucket_info(1).atom_size()); - ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0)); - ASSERT_EQ(0, data.bucket_info(1).wall_clock_timestamp_nanos_size()); - 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); - - ASSERT_EQ(1, data.bucket_info(2).atom_size()); - ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 4, data.bucket_info(2).elapsed_timestamp_nanos(0)); - ASSERT_EQ(0, data.bucket_info(2).wall_clock_timestamp_nanos_size()); - 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); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp deleted file mode 100644 index a40a9484a841..000000000000 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp +++ /dev/null @@ -1,295 +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. - -#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" - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sampling_type) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); - *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); - - auto atomMatcher = CreateSimpleAtomMatcher("", util::APP_START_OCCURRED); - *config.add_atom_matcher() = atomMatcher; - - auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); - *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); - *config.add_predicate() = isInBackgroundPredicate; - - auto gaugeMetric = config.add_gauge_metric(); - gaugeMetric->set_id(123456); - gaugeMetric->set_what(atomMatcher.id()); - gaugeMetric->set_condition(isInBackgroundPredicate.id()); - gaugeMetric->mutable_gauge_fields_filter()->set_include_all(false); - gaugeMetric->set_sampling_type(sampling_type); - auto fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields(); - fieldMatcher->set_field(util::APP_START_OCCURRED); - fieldMatcher->add_child()->set_field(3); // type (enum) - fieldMatcher->add_child()->set_field(4); // activity_name(str) - fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64) - *gaugeMetric->mutable_dimensions_in_what() = - CreateDimensions(util::APP_START_OCCURRED, {1 /* uid field */ }); - gaugeMetric->set_bucket(FIVE_MINUTES); - - auto links = gaugeMetric->add_links(); - links->set_condition(isInBackgroundPredicate.id()); - auto dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(util::APP_START_OCCURRED); - dimensionWhat->add_child()->set_field(1); // uid field. - auto dimensionCondition = links->mutable_fields_in_condition(); - dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED); - dimensionCondition->add_child()->set_field(1); // uid field. - return config; -} - -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, 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); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -} // namespace - -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); - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), - &gaugeMetrics); - ASSERT_EQ(2, gaugeMetrics.data_size()); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(util::APP_START_OCCURRED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_EQ(3, data.bucket_info_size()); - if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) { - ASSERT_EQ(2, data.bucket_info(0).atom_size()); - ASSERT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size()); - ASSERT_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()); - - ASSERT_EQ(1, data.bucket_info(1).atom_size()); - ASSERT_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()); - - ASSERT_EQ(2, data.bucket_info(2).atom_size()); - ASSERT_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 { - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_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()); - - ASSERT_EQ(1, data.bucket_info(1).atom_size()); - ASSERT_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()); - - ASSERT_EQ(1, data.bucket_info(2).atom_size()); - ASSERT_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(), util::APP_START_OCCURRED); - ASSERT_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()); - ASSERT_EQ(1, data.bucket_info_size()); - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_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"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp deleted file mode 100644 index e320419a9d44..000000000000 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ /dev/null @@ -1,1833 +0,0 @@ -// 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. - -#include <gtest/gtest.h> - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - - *config.add_atom_matcher() = saverModeMatcher; - *config.add_atom_matcher() = crashMatcher; - *config.add_atom_matcher() = screenOnMatcher; - - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(crashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - 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(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - auto event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - - return config; -} - -StatsdConfig CreateStatsdConfigWithOneDeactivation() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher(); - - *config.add_atom_matcher() = saverModeMatcher; - *config.add_atom_matcher() = crashMatcher; - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher; - - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(crashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - 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(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - auto event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - - return config; -} - -StatsdConfig CreateStatsdConfigWithTwoDeactivations() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher(); - auto brightnessChangedMatcher2 = CreateScreenBrightnessChangedAtomMatcher(); - brightnessChangedMatcher2.set_id(StringToId("ScreenBrightnessChanged2")); - - *config.add_atom_matcher() = saverModeMatcher; - *config.add_atom_matcher() = crashMatcher; - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher2; - - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(crashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - 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(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - auto event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id()); - - return config; -} - -StatsdConfig CreateStatsdConfigWithSameDeactivations() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher(); - - *config.add_atom_matcher() = saverModeMatcher; - *config.add_atom_matcher() = crashMatcher; - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher; - - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(crashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - 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(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - auto event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - - return config; -} - -StatsdConfig CreateStatsdConfigWithTwoMetricsTwoDeactivations() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - auto foregroundMatcher = CreateMoveToForegroundAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher(); - auto brightnessChangedMatcher2 = CreateScreenBrightnessChangedAtomMatcher(); - brightnessChangedMatcher2.set_id(StringToId("ScreenBrightnessChanged2")); - - *config.add_atom_matcher() = saverModeMatcher; - *config.add_atom_matcher() = crashMatcher; - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher2; - *config.add_atom_matcher() = foregroundMatcher; - - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(crashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - int64_t metricId2 = 234567; - countMetric = config.add_count_metric(); - countMetric->set_id(metricId2); - countMetric->set_what(foregroundMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::ACTIVITY_FOREGROUND_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - 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(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - auto event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id()); - - metric_activation1 = config.add_metric_activation(); - metric_activation1->set_metric_id(metricId2); - event_activation1 = metric_activation1->add_event_activation(); - event_activation1->set_atom_matcher_id(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id()); - - return config; -} - -} // namespace - -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); - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_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). - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(4, countMetrics.data_size()); - - auto data = countMetrics.data(0); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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); - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_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). - ASSERT_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); - ASSERT_EQ(eventDeactivationMap.size(), 1u); - EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(5, countMetrics.data_size()); - - auto data = countMetrics.data(0); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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); - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_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). - ASSERT_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); - ASSERT_EQ(eventDeactivationMap.size(), 2u); - EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); - EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); - ASSERT_EQ(eventDeactivationMap[3].size(), 1u); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(5, countMetrics.data_size()); - - auto data = countMetrics.data(0); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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); - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_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). - ASSERT_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); - ASSERT_EQ(eventDeactivationMap.size(), 1u); - EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(3, countMetrics.data_size()); - - auto data = countMetrics.data(0); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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); - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_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). - ASSERT_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); - ASSERT_EQ(eventDeactivationMap.size(), 2u); - EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); - EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); - ASSERT_EQ(eventDeactivationMap[3].size(), 1u); - ASSERT_EQ(eventDeactivationMap[4].size(), 1u); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - ASSERT_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); - ASSERT_EQ(eventDeactivationMap2.size(), 2u); - EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end()); - EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end()); - ASSERT_EQ(eventDeactivationMap[3].size(), 1u); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(2, reports.reports(0).metrics_size()); - - StatsLogReport::CountMetricDataWrapper countMetrics; - - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(5, countMetrics.data_size()); - - auto data = countMetrics.data(0); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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); - ASSERT_EQ(5, countMetrics.data_size()); - - data = countMetrics.data(0); - EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp deleted file mode 100644 index 5e77ee0f0b0a..000000000000 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ /dev/null @@ -1,348 +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. - -#include <gtest/gtest.h> - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ -namespace { - -StatsdConfig CreateStatsdConfig() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); - *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); - - *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); - *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); - - auto appCrashMatcher = CreateProcessCrashAtomMatcher(); - *config.add_atom_matcher() = appCrashMatcher; - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - - auto isSyncingPredicate = CreateIsSyncingPredicate(); - auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidDimensions( - util::SYNC_STATE_CHANGED, {Position::FIRST}); - syncDimension->add_child()->set_field(2 /* name field*/); - - auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); - *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); - - *config.add_predicate() = screenIsOffPredicate; - *config.add_predicate() = isSyncingPredicate; - *config.add_predicate() = isInBackgroundPredicate; - - auto combinationPredicate = config.add_predicate(); - combinationPredicate->set_id(StringToId("combinationPredicate")); - combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); - addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); - addPredicateToPredicateCombination(isInBackgroundPredicate, combinationPredicate); - - auto countMetric = config.add_count_metric(); - countMetric->set_id(StringToId("AppCrashes")); - countMetric->set_what(appCrashMatcher.id()); - countMetric->set_condition(combinationPredicate->id()); - // The metric is dimensioning by uid only. - *countMetric->mutable_dimensions_in_what() = - CreateDimensions(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1}); - countMetric->set_bucket(FIVE_MINUTES); - - // Links between crash atom and condition of app is in syncing. - auto links = countMetric->add_links(); - links->set_condition(isSyncingPredicate.id()); - auto dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - util::SYNC_STATE_CHANGED, {Position::FIRST}); - - // Links between crash atom and condition of app is in background. - links = countMetric->add_links(); - links->set_condition(isInBackgroundPredicate.id()); - dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - dimensionWhat->add_child()->set_field(1); // uid field. - auto dimensionCondition = links->mutable_fields_in_condition(); - dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED); - dimensionCondition->add_child()->set_field(1); // uid field. - return config; -} -} // namespace - -// 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); - ASSERT_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); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); - ASSERT_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(), util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); - ASSERT_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(), util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - ASSERT_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"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp deleted file mode 100644 index c03b9254f70d..000000000000 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ /dev/null @@ -1,433 +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. - -#include <android/binder_ibinder.h> -#include <android/binder_interface_utils.h> -#include <gtest/gtest.h> - -#include <vector> - -#include "src/StatsLogProcessor.h" -#include "src/StatsService.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -using::ndk::SharedRefBase; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ -namespace { -const string kApp1 = "app1.sharing.1"; -const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs. -const int kCallingUid = 0; // Randomly chosen - -void SendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) { - string str; - config.SerializeToString(&str); - std::vector<uint8_t> configAsVec(str.begin(), str.end()); - service->addConfiguration(kConfigKey, configAsVec, kCallingUid); -} - -ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp, - bool include_current = false) { - vector<uint8_t> output; - ConfigKey configKey(AIBinder_getCallingUid(), kConfigKey); - processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/, - true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output); - ConfigMetricsReportList reports; - reports.ParseFromArray(output.data(), output.size()); - EXPECT_EQ(1, reports.reports_size()); - return reports.reports(kCallingUid); -} - -StatsdConfig MakeConfig() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto appCrashMatcher = CreateProcessCrashAtomMatcher(); - *config.add_atom_matcher() = appCrashMatcher; - auto countMetric = config.add_count_metric(); - countMetric->set_id(StringToId("AppCrashes")); - countMetric->set_what(appCrashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - return config; -} - -StatsdConfig MakeValueMetricConfig(int64_t minTime) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - - auto pulledAtomMatcher = - CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto valueMetric = config.add_value_metric(); - valueMetric->set_id(123456); - valueMetric->set_what(pulledAtomMatcher.id()); - *valueMetric->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - *valueMetric->mutable_dimensions_in_what() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); - valueMetric->set_bucket(FIVE_MINUTES); - valueMetric->set_min_bucket_size_nanos(minTime); - valueMetric->set_use_absolute_value_on_reset(true); - valueMetric->set_skip_zero_diff_output(false); - return config; -} - -StatsdConfig MakeGaugeMetricConfig(int64_t minTime) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - - auto pulledAtomMatcher = - CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto gaugeMetric = config.add_gauge_metric(); - gaugeMetric->set_id(123456); - gaugeMetric->set_what(pulledAtomMatcher.id()); - gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true); - *gaugeMetric->mutable_dimensions_in_what() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); - gaugeMetric->set_bucket(FIVE_MINUTES); - gaugeMetric->set_min_bucket_size_nanos(minTime); - return config; -} -} // anonymous namespace - -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. - ASSERT_EQ(1, report.metrics_size()); - ASSERT_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); - ASSERT_EQ(1, report.metrics_size()); - ASSERT_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, TestCountMetricSplitOnBoot) { - 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. - - // Goes into the first bucket - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + NS_PER_SEC, 100).get()); - int64_t bootCompleteTimeNs = start + 2 * NS_PER_SEC; - service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs); - // Goes into the second bucket. - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3 * NS_PER_SEC, 100).get()); - - ConfigMetricsReport report = GetReports(service->mProcessor, start + 4 * NS_PER_SEC); - 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_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)), - report.metrics(0).count_metrics().data(0).bucket_info(0).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); - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make<FakeSubsystemSleepCallback>()); - // Partial buckets don't occur when app is first installed. - service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); - SendConfig(service, MakeValueMetricConfig(0)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - int64_t appUpgradeTimeNs = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC; - service->mUidMap->updateApp(appUpgradeTimeNs, String16(kApp1.c_str()), 1, 2, String16("v2"), - String16("")); - - ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); - backfillStartEndTimestamp(&report); - - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(0, report.metrics(0).value_metrics().skipped_size()); - - // The fake subsystem state sleep puller returns two atoms. - ASSERT_EQ(2, report.metrics(0).value_metrics().data_size()); - ASSERT_EQ(2, report.metrics(0).value_metrics().data(0).bucket_info_size()); - EXPECT_EQ(MillisToNano(NanoToMillis(appUpgradeTimeNs)), - report.metrics(0).value_metrics().data(0).bucket_info(1).end_bucket_elapsed_nanos()); -} - -TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make<FakeSubsystemSleepCallback>()); - // Partial buckets don't occur when app is first installed. - service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); - SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC; - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"), - String16("")); - - ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); - backfillStartEndTimestamp(&report); - - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size()); - EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos()); - // Can't test the start time since it will be based on the actual time when the pulling occurs. - EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)), - report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos()); - - ASSERT_EQ(2, report.metrics(0).value_metrics().data_size()); - ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size()); -} - -TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket) { - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - // Initial pull will fail since puller is not registered. - SendConfig(service, MakeValueMetricConfig(0)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make<FakeSubsystemSleepCallback>()); - - int64_t bootCompleteTimeNs = start + NS_PER_SEC; - service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs); - - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - - ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); - backfillStartEndTimestamp(&report); - - // First bucket is dropped due to the initial pull failing - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size()); - EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)), - report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos()); - - // The fake subsystem state sleep puller returns two atoms. - ASSERT_EQ(2, report.metrics(0).value_metrics().data_size()); - ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size()); - EXPECT_EQ( - MillisToNano(NanoToMillis(bootCompleteTimeNs)), - report.metrics(0).value_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos()); -} - -TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make<FakeSubsystemSleepCallback>()); - // Partial buckets don't occur when app is first installed. - service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); - SendConfig(service, MakeGaugeMetricConfig(0)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2, - String16("v2"), String16("")); - - ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); - backfillStartEndTimestamp(&report); - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(0, report.metrics(0).gauge_metrics().skipped_size()); - // The fake subsystem state sleep puller returns two atoms. - ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size()); - ASSERT_EQ(2, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); -} - -TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - // Partial buckets don't occur when app is first installed. - service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make<FakeSubsystemSleepCallback>()); - SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2; - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"), - String16("")); - - ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); - backfillStartEndTimestamp(&report); - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size()); - // Can't test the start time since it will be based on the actual time when the pulling occurs. - EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos()); - EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)), - report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos()); - ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size()); - ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); -} - -TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket) { - shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); - // Initial pull will fail since puller hasn't been registered. - SendConfig(service, MakeGaugeMetricConfig(0)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make<FakeSubsystemSleepCallback>()); - - int64_t bootCompleteTimeNs = start + NS_PER_SEC; - service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs); - - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - - ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); - backfillStartEndTimestamp(&report); - - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(0, report.metrics(0).gauge_metrics().skipped_size()); - // The fake subsystem state sleep puller returns two atoms. - ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size()); - // No data in the first bucket, so nothing is reported - ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); - EXPECT_EQ( - MillisToNano(NanoToMillis(bootCompleteTimeNs)), - report.metrics(0).gauge_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp deleted file mode 100644 index 4d3928277527..000000000000 --- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp +++ /dev/null @@ -1,679 +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. - -#include <android/binder_interface_utils.h> -#include <gtest/gtest.h> - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include <vector> - -using ::ndk::SharedRefBase; - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -const int64_t metricId = 123456; - -StatsdConfig CreateStatsdConfig(bool useCondition = true) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - auto pulledAtomMatcher = - CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = screenIsOffPredicate; - - auto valueMetric = config.add_value_metric(); - valueMetric->set_id(metricId); - valueMetric->set_what(pulledAtomMatcher.id()); - if (useCondition) { - valueMetric->set_condition(screenIsOffPredicate.id()); - } - *valueMetric->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - *valueMetric->mutable_dimensions_in_what() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); - valueMetric->set_bucket(FIVE_MINUTES); - valueMetric->set_use_absolute_value_on_reset(true); - valueMetric->set_skip_zero_diff_output(false); - valueMetric->set_max_pull_delay_sec(INT_MAX); - return config; -} - -StatsdConfig CreateStatsdConfigWithStates() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - - auto pulledAtomMatcher = CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = CreateBatteryStateNoneMatcher(); - *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); - - auto screenOnPredicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = screenOnPredicate; - - auto screenOffPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = screenOffPredicate; - - auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate(); - *config.add_predicate() = deviceUnpluggedPredicate; - - auto screenOnOnBatteryPredicate = config.add_predicate(); - screenOnOnBatteryPredicate->set_id(StringToId("screenOnOnBatteryPredicate")); - screenOnOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenOnPredicate, screenOnOnBatteryPredicate); - addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOnOnBatteryPredicate); - - auto screenOffOnBatteryPredicate = config.add_predicate(); - screenOffOnBatteryPredicate->set_id(StringToId("ScreenOffOnBattery")); - screenOffOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenOffPredicate, screenOffOnBatteryPredicate); - addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOffOnBatteryPredicate); - - const State screenState = - CreateScreenStateWithSimpleOnOffMap(/*screen on id=*/321, /*screen off id=*/123); - *config.add_state() = screenState; - - // ValueMetricSubsystemSleepWhileScreenOnOnBattery - auto valueMetric1 = config.add_value_metric(); - valueMetric1->set_id(metricId); - valueMetric1->set_what(pulledAtomMatcher.id()); - valueMetric1->set_condition(screenOnOnBatteryPredicate->id()); - *valueMetric1->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - valueMetric1->set_bucket(FIVE_MINUTES); - valueMetric1->set_use_absolute_value_on_reset(true); - valueMetric1->set_skip_zero_diff_output(false); - valueMetric1->set_max_pull_delay_sec(INT_MAX); - - // ValueMetricSubsystemSleepWhileScreenOffOnBattery - ValueMetric* valueMetric2 = config.add_value_metric(); - valueMetric2->set_id(StringToId("ValueMetricSubsystemSleepWhileScreenOffOnBattery")); - valueMetric2->set_what(pulledAtomMatcher.id()); - valueMetric2->set_condition(screenOffOnBatteryPredicate->id()); - *valueMetric2->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - valueMetric2->set_bucket(FIVE_MINUTES); - valueMetric2->set_use_absolute_value_on_reset(true); - valueMetric2->set_skip_zero_diff_output(false); - valueMetric2->set_max_pull_delay_sec(INT_MAX); - - // ValueMetricSubsystemSleepWhileOnBatterySliceScreen - ValueMetric* valueMetric3 = config.add_value_metric(); - valueMetric3->set_id(StringToId("ValueMetricSubsystemSleepWhileOnBatterySliceScreen")); - valueMetric3->set_what(pulledAtomMatcher.id()); - valueMetric3->set_condition(deviceUnpluggedPredicate.id()); - *valueMetric3->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - valueMetric3->add_slice_by_state(screenState.id()); - valueMetric3->set_bucket(FIVE_MINUTES); - valueMetric3->set_use_absolute_value_on_reset(true); - valueMetric3->set_skip_zero_diff_output(false); - valueMetric3->set_max_pull_delay_sec(INT_MAX); - return config; -} - -} // namespace - -/** - * Tests the initial condition and condition after the first log events for - * value metrics with either a combination condition or simple condition. - * - * Metrics should be initialized with condition kUnknown (given that the - * predicate is using the default InitialValue of UNKNOWN). The condition should - * be updated to either kFalse or kTrue if a condition event is logged for all - * children conditions. - */ -TEST(ValueMetricE2eTest, TestInitialConditionChanges) { - StatsdConfig config = CreateStatsdConfigWithStates(); - 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; - int32_t tagId = util::SUBSYSTEM_SLEEP_STATE; - auto processor = - CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, - SharedRefBase::make<FakeSubsystemSleepCallback>(), tagId); - - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - EXPECT_EQ(3, metricsManager->mAllMetricProducers.size()); - - // Combination condition metric - screen on and device unplugged - sp<MetricProducer> metricProducer1 = metricsManager->mAllMetricProducers[0]; - // Simple condition metric - device unplugged - sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[2]; - - EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); - - auto screenOnEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 30, android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); - - auto screenOffEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 40, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); - - auto pluggedUsbEvent = CreateBatteryStateChangedEvent( - configAddedTimeNs + 50, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); - processor->OnLogEvent(pluggedUsbEvent.get()); - EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kFalse, metricProducer2->mCondition); - - auto pluggedNoneEvent = CreateBatteryStateChangedEvent( - configAddedTimeNs + 70, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE); - processor->OnLogEvent(pluggedNoneEvent.get()); - EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition); -} - -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>(), - util::SUBSYSTEM_SLEEP_STATE); - ASSERT_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. - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::ValueMetricDataWrapper valueMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); - ASSERT_GT((int)valueMetrics.data_size(), 1); - - auto data = valueMetrics.data(0); - EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); - ASSERT_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. - ASSERT_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()); - ASSERT_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()); - ASSERT_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()); - ASSERT_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()); - ASSERT_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>(), - util::SUBSYSTEM_SLEEP_STATE); - ASSERT_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. - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::ValueMetricDataWrapper valueMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); - ASSERT_GT((int)valueMetrics.data_size(), 1); - - auto data = valueMetrics.data(0); - EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); - ASSERT_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()); - ASSERT_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()); - ASSERT_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()); - ASSERT_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()); - ASSERT_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>(), - util::SUBSYSTEM_SLEEP_STATE); - ASSERT_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. - ASSERT_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); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::ValueMetricDataWrapper valueMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); - ASSERT_GT((int)valueMetrics.data_size(), 0); - - auto data = valueMetrics.data(0); - EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); - ASSERT_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. - ASSERT_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()); - ASSERT_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()); - ASSERT_EQ(1, bucketInfo.values_size()); -} - -/** - * Test initialization of a simple value metric that is sliced by a state. - * - * ValueCpuUserTimePerScreenState - */ -TEST(ValueMetricE2eTest, TestInitWithSlicedState) { - // Create config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto pulledAtomMatcher = - CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - - auto screenState = CreateScreenState(); - *config.add_state() = screenState; - - // Create value metric that slices by screen state without a map. - int64_t metricId = 123456; - auto valueMetric = config.add_value_metric(); - valueMetric->set_id(metricId); - valueMetric->set_bucket(TimeUnit::FIVE_MINUTES); - valueMetric->set_what(pulledAtomMatcher.id()); - *valueMetric->mutable_value_field() = - CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */}); - valueMetric->add_slice_by_state(screenState.id()); - valueMetric->set_max_pull_delay_sec(INT_MAX); - - // Initialize StatsLogProcessor. - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - const uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.value_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 ValueMetricProducer was initialized correctly. - ASSERT_EQ(1U, processor->mMetricsManagers.size()); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(1, metricsManager->mAllMetricProducers.size()); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(1, metricProducer->mSlicedStateAtoms.size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0)); - ASSERT_EQ(0, metricProducer->mStateGroupMap.size()); -} - -/** - * Test initialization of a value metric that is sliced by state and has - * dimensions_in_what. - * - * ValueCpuUserTimePerUidPerUidProcessState - */ -TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions) { - // Create config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto cpuTimePerUidMatcher = - CreateSimpleAtomMatcher("CpuTimePerUidMatcher", util::CPU_TIME_PER_UID); - *config.add_atom_matcher() = cpuTimePerUidMatcher; - - auto uidProcessState = CreateUidProcessState(); - *config.add_state() = uidProcessState; - - // Create value metric that slices by screen state with a complete map. - int64_t metricId = 123456; - auto valueMetric = config.add_value_metric(); - valueMetric->set_id(metricId); - valueMetric->set_bucket(TimeUnit::FIVE_MINUTES); - valueMetric->set_what(cpuTimePerUidMatcher.id()); - *valueMetric->mutable_value_field() = - CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */}); - *valueMetric->mutable_dimensions_in_what() = - CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */}); - valueMetric->add_slice_by_state(uidProcessState.id()); - MetricStateLink* stateLink = valueMetric->add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - valueMetric->set_max_pull_delay_sec(INT_MAX); - - // Initialize StatsLogProcessor. - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - 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 ValueMetricProducer was initialized correctly. - ASSERT_EQ(1U, processor->mMetricsManagers.size()); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(1, metricsManager->mAllMetricProducers.size()); - sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(1, metricProducer->mSlicedStateAtoms.size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0)); - ASSERT_EQ(0, metricProducer->mStateGroupMap.size()); -} - -/** - * Test initialization of a value metric that is sliced by state and has - * dimensions_in_what. - * - * ValueCpuUserTimePerUidPerUidProcessState - */ -TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions) { - // Create config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto cpuTimePerUidMatcher = - CreateSimpleAtomMatcher("CpuTimePerUidMatcher", util::CPU_TIME_PER_UID); - *config.add_atom_matcher() = cpuTimePerUidMatcher; - - auto uidProcessState = CreateUidProcessState(); - *config.add_state() = uidProcessState; - - // Create value metric that slices by screen state with a complete map. - int64_t metricId = 123456; - auto valueMetric = config.add_value_metric(); - valueMetric->set_id(metricId); - valueMetric->set_bucket(TimeUnit::FIVE_MINUTES); - valueMetric->set_what(cpuTimePerUidMatcher.id()); - *valueMetric->mutable_value_field() = - CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */}); - valueMetric->add_slice_by_state(uidProcessState.id()); - MetricStateLink* stateLink = valueMetric->add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - valueMetric->set_max_pull_delay_sec(INT_MAX); - - // Initialize StatsLogProcessor. - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - // No StateTrackers are initialized. - EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount()); - - // Config initialization fails. - ASSERT_EQ(0, processor->mMetricsManagers.size()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp deleted file mode 100644 index 52bc222e5fe2..000000000000 --- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp +++ /dev/null @@ -1,355 +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. - -#include <gtest/gtest.h> - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = screenIsOffPredicate; - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - // The predicate is dimensioning by any attribution node and both by uid and tag. - FieldMatcher dimensions = CreateAttributionUidAndTagDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST, Position::LAST}); - // Also slice by the wakelock tag - dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock. - *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; - *config.add_predicate() = holdingWakelockPredicate; - - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("WakelockDuration")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->set_condition(screenIsOffPredicate.id()); - durationMetric->set_aggregation_type(aggregationType); - // The metric is dimensioning by first attribution node and only by uid. - *durationMetric->mutable_dimensions_in_what() = - CreateAttributionUidDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - durationMetric->set_bucket(FIVE_MINUTES); - return config; -} - -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 - -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); - ASSERT_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); - - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - // Only 1 dimension output. The tag dimension in the predicate has been aggregated. - ASSERT_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(), - util::WAKELOCK_STATE_CHANGED, 111); - // Validate bucket info. - ASSERT_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); - ASSERT_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); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - // Dump the report after the end of 2nd bucket. - ASSERT_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(), - 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); - ASSERT_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); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - ASSERT_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(), - 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); - ASSERT_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); - - ASSERT_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. - ASSERT_EQ(1, reports.reports(0).metrics_size()); - ASSERT_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); - ASSERT_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); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_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. - ASSERT_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(), - 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); - ASSERT_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); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - ASSERT_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(), - 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"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp deleted file mode 100644 index 85a60886349e..000000000000 --- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/external/StatsCallbackPuller.h" - -#include <aidl/android/os/BnPullAtomCallback.h> -#include <aidl/android/os/IPullAtomResultReceiver.h> -#include <aidl/android/util/StatsEventParcel.h> -#include <android/binder_interface_utils.h> -#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__ - -namespace android { -namespace os { -namespace statsd { - -using namespace testing; -using Status = ::ndk::ScopedAStatus; -using aidl::android::os::BnPullAtomCallback; -using aidl::android::os::IPullAtomResultReceiver; -using aidl::android::util::StatsEventParcel; -using ::ndk::SharedRefBase; -using std::make_shared; -using std::shared_ptr; -using std::vector; -using std::this_thread::sleep_for; -using testing::Contains; - -namespace { -int pullTagId = -12; -bool pullSuccess; -vector<int64_t> values; -int64_t pullDelayNs; -int64_t pullTimeoutNs; -int64_t pullCoolDownNs; -std::thread pullThread; - -AStatsEvent* createSimpleEvent(int64_t value) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, pullTagId); - AStatsEvent_writeInt64(event, value); - AStatsEvent_build(event); - return event; -} - -void executePull(const shared_ptr<IPullAtomResultReceiver>& resultReceiver) { - // Convert stats_events into StatsEventParcels. - vector<StatsEventParcel> parcels; - for (int i = 0; i < values.size(); i++) { - AStatsEvent* event = createSimpleEvent(values[i]); - size_t size; - uint8_t* buffer = AStatsEvent_getBuffer(event, &size); - - StatsEventParcel p; - // vector.assign() creates a copy, but this is inevitable unless - // stats_event.h/c uses a vector as opposed to a buffer. - p.buffer.assign(buffer, buffer + size); - parcels.push_back(std::move(p)); - AStatsEvent_release(event); - } - - sleep_for(std::chrono::nanoseconds(pullDelayNs)); - resultReceiver->pullFinished(pullTagId, pullSuccess, parcels); -} - -class FakePullAtomCallback : public BnPullAtomCallback { -public: - Status onPullAtom(int atomTag, - const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override { - // Force pull to happen in separate thread to simulate binder. - pullThread = std::thread(executePull, resultReceiver); - return Status::ok(); - } -}; - -class StatsCallbackPullerTest : public ::testing::Test { -public: - StatsCallbackPullerTest() { - } - - void SetUp() override { - pullSuccess = false; - pullDelayNs = 0; - values.clear(); - pullTimeoutNs = 10000000000LL; // 10 seconds. - pullCoolDownNs = 1000000000; // 1 second. - } - - void TearDown() override { - if (pullThread.joinable()) { - pullThread.join(); - } - values.clear(); - } -}; -} // Anonymous namespace. - -TEST_F(StatsCallbackPullerTest, PullSuccess) { - shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>(); - int64_t value = 43; - pullSuccess = true; - values.push_back(value); - - StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {}); - - vector<std::shared_ptr<LogEvent>> dataHolder; - int64_t startTimeNs = getElapsedRealtimeNs(); - EXPECT_TRUE(puller.PullInternal(&dataHolder)); - int64_t endTimeNs = getElapsedRealtimeNs(); - - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_LT(startTimeNs, dataHolder[0]->GetElapsedTimestampNs()); - EXPECT_GT(endTimeNs, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(value, dataHolder[0]->getValues()[0].mValue.int_value); -} - -TEST_F(StatsCallbackPullerTest, PullFail) { - shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>(); - pullSuccess = false; - int64_t value = 1234; - values.push_back(value); - - StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {}); - - vector<shared_ptr<LogEvent>> dataHolder; - EXPECT_FALSE(puller.PullInternal(&dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -TEST_F(StatsCallbackPullerTest, PullTimeout) { - shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>(); - pullSuccess = true; - pullDelayNs = MillisToNano(5); // 5ms. - pullTimeoutNs = 10000; // 10 microseconds. - int64_t value = 4321; - values.push_back(value); - - StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {}); - - vector<shared_ptr<LogEvent>> dataHolder; - int64_t startTimeNs = getElapsedRealtimeNs(); - // Returns true to let StatsPuller code evaluate the timeout. - EXPECT_TRUE(puller.PullInternal(&dataHolder)); - int64_t endTimeNs = getElapsedRealtimeNs(); - int64_t actualPullDurationNs = endTimeNs - startTimeNs; - - // Pull should take at least the timeout amount of time, but should stop early because the delay - // is bigger. - EXPECT_LT(pullTimeoutNs, actualPullDurationNs); - EXPECT_GT(pullDelayNs, actualPullDurationNs); - ASSERT_EQ(0, dataHolder.size()); - - // Let the pull return and make sure that the dataHolder is not modified. - pullThread.join(); - ASSERT_EQ(0, dataHolder.size()); -} - -// Register a puller and ensure that the timeout logic works. -TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) { - shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>(); - pullSuccess = true; - pullDelayNs = MillisToNano(5); // 5 ms. - pullTimeoutNs = 10000; // 10 microsseconds. - int64_t value = 4321; - int32_t uid = 123; - values.push_back(value); - - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - pullerManager->RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs, - vector<int32_t>(), cb); - vector<shared_ptr<LogEvent>> dataHolder; - int64_t startTimeNs = getElapsedRealtimeNs(); - // Returns false, since StatsPuller code will evaluate the timeout. - EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, startTimeNs, &dataHolder)); - int64_t endTimeNs = getElapsedRealtimeNs(); - int64_t actualPullDurationNs = endTimeNs - startTimeNs; - - // Pull should take at least the timeout amount of time, but should stop early because the delay - // is bigger. Make sure that the time is closer to the timeout, than to the intended delay. - EXPECT_LT(pullTimeoutNs, actualPullDurationNs); - EXPECT_GT(pullDelayNs / 5, actualPullDurationNs); - ASSERT_EQ(0, dataHolder.size()); - - // Let the pull return and make sure that the dataHolder is not modified. - pullThread.join(); - ASSERT_EQ(0, dataHolder.size()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/external/StatsPullerManager_test.cpp b/cmds/statsd/tests/external/StatsPullerManager_test.cpp deleted file mode 100644 index 0d539f477016..000000000000 --- a/cmds/statsd/tests/external/StatsPullerManager_test.cpp +++ /dev/null @@ -1,150 +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. - -#include "src/external/StatsPullerManager.h" - -#include <aidl/android/os/IPullAtomResultReceiver.h> -#include <aidl/android/util/StatsEventParcel.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -using aidl::android::util::StatsEventParcel; -using ::ndk::SharedRefBase; -using std::make_shared; -using std::shared_ptr; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -namespace { - -int pullTagId1 = 10101; -int pullTagId2 = 10102; -int uid1 = 9999; -int uid2 = 8888; -ConfigKey configKey(50, 12345); -ConfigKey badConfigKey(60, 54321); -int unregisteredUid = 98765; -int64_t coolDownNs = NS_PER_SEC; -int64_t timeoutNs = NS_PER_SEC / 2; - -AStatsEvent* createSimpleEvent(int32_t atomId, int32_t value) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, atomId); - AStatsEvent_writeInt32(event, value); - AStatsEvent_build(event); - return event; -} - -class FakePullAtomCallback : public BnPullAtomCallback { -public: - FakePullAtomCallback(int32_t uid) : mUid(uid){}; - Status onPullAtom(int atomTag, - const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override { - vector<StatsEventParcel> parcels; - AStatsEvent* event = createSimpleEvent(atomTag, mUid); - size_t size; - uint8_t* buffer = AStatsEvent_getBuffer(event, &size); - - StatsEventParcel p; - // vector.assign() creates a copy, but this is inevitable unless - // stats_event.h/c uses a vector as opposed to a buffer. - p.buffer.assign(buffer, buffer + size); - parcels.push_back(std::move(p)); - AStatsEvent_release(event); - resultReceiver->pullFinished(atomTag, /*success*/ true, parcels); - return Status::ok(); - } - int32_t mUid; -}; - -class FakePullUidProvider : public PullUidProvider { -public: - vector<int32_t> getPullAtomUids(int atomId) override { - if (atomId == pullTagId1) { - return {uid2, uid1}; - } else if (atomId == pullTagId2) { - return {uid2}; - } - return {}; - } -}; - -sp<StatsPullerManager> createPullerManagerAndRegister() { - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - shared_ptr<FakePullAtomCallback> cb1 = SharedRefBase::make<FakePullAtomCallback>(uid1); - pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1); - shared_ptr<FakePullAtomCallback> cb2 = SharedRefBase::make<FakePullAtomCallback>(uid2); - pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2); - pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1); - return pullerManager; -} -} // anonymous namespace - -TEST(StatsPullerManagerTest, TestPullInvalidUid) { - sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister(); - - vector<shared_ptr<LogEvent>> data; - EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data)); -} - -TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) { - sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister(); - - vector<shared_ptr<LogEvent>> data; - EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data)); - ASSERT_EQ(data.size(), 1); - EXPECT_EQ(data[0]->GetTagId(), pullTagId1); - ASSERT_EQ(data[0]->getValues().size(), 1); - EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid1); -} - -TEST(StatsPullerManagerTest, TestPullInvalidConfigKey) { - sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister(); - sp<FakePullUidProvider> uidProvider = new FakePullUidProvider(); - pullerManager->RegisterPullUidProvider(configKey, uidProvider); - - vector<shared_ptr<LogEvent>> data; - EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data)); -} - -TEST(StatsPullerManagerTest, TestPullConfigKeyGood) { - sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister(); - sp<FakePullUidProvider> uidProvider = new FakePullUidProvider(); - pullerManager->RegisterPullUidProvider(configKey, uidProvider); - - vector<shared_ptr<LogEvent>> data; - EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data)); - EXPECT_EQ(data[0]->GetTagId(), pullTagId1); - ASSERT_EQ(data[0]->getValues().size(), 1); - EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2); -} - -TEST(StatsPullerManagerTest, TestPullConfigKeyNoPullerWithUid) { - sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister(); - sp<FakePullUidProvider> uidProvider = new FakePullUidProvider(); - pullerManager->RegisterPullUidProvider(configKey, uidProvider); - - vector<shared_ptr<LogEvent>> data; - EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data)); -} - -} // namespace statsd -} // namespace os -} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp deleted file mode 100644 index 55a90365e151..000000000000 --- a/cmds/statsd/tests/external/StatsPuller_test.cpp +++ /dev/null @@ -1,316 +0,0 @@ -// 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. - -#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__ - -namespace android { -namespace os { -namespace statsd { - -using namespace testing; -using std::make_shared; -using std::shared_ptr; -using std::vector; -using std::this_thread::sleep_for; -using testing::Contains; - -namespace { -int pullTagId = 10014; - -bool pullSuccess; -vector<std::shared_ptr<LogEvent>> pullData; -long pullDelayNs; - -class FakePuller : public StatsPuller { -public: - FakePuller() - : StatsPuller(pullTagId, /*coolDownNs=*/MillisToNano(10), /*timeoutNs=*/MillisToNano(5)){}; - -private: - bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override { - (*data) = pullData; - sleep_for(std::chrono::nanoseconds(pullDelayNs)); - return pullSuccess; - } -}; - -FakePuller puller; - -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); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -class StatsPullerTest : public ::testing::Test { -public: - StatsPullerTest() { - } - - void SetUp() override { - puller.ForceClearCache(); - pullSuccess = false; - pullDelayNs = 0; - pullData.clear(); - } -}; - -} // Anonymous namespace. - -TEST_F(StatsPullerTest, PullSuccess) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = true; - - vector<std::shared_ptr<LogEvent>> dataHolder; - EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); - - sleep_for(std::chrono::milliseconds(11)); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - pullSuccess = true; - - EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_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(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); - - sleep_for(std::chrono::milliseconds(11)); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - pullSuccess = false; - dataHolder.clear(); - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); - - // Fails due to hitting the cool down. - pullSuccess = true; - dataHolder.clear(); - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_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 5ms - pullDelayNs = MillisToNano(6); - - vector<std::shared_ptr<LogEvent>> dataHolder; - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - pullDelayNs = 0; - - pullSuccess = true; - dataHolder.clear(); - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_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(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -TEST_F(StatsPullerTest, PullTakeTooLong) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = true; - pullDelayNs = MillisToNano(6); - - vector<std::shared_ptr<LogEvent>> dataHolder; - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_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(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_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(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_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(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - pullSuccess = true; - - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -TEST_F(StatsPullerTest, PullSameEventTime) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = true; - int64_t eventTimeNs = getElapsedRealtimeNs(); - - vector<std::shared_ptr<LogEvent>> dataHolder; - EXPECT_TRUE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - // Sleep to ensure the cool down expires. - sleep_for(std::chrono::milliseconds(11)); - pullSuccess = true; - - dataHolder.clear(); - EXPECT_TRUE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -} - -// Test pull takes longer than timeout, 2nd pull happens at same event time -TEST_F(StatsPullerTest, PullTakeTooLongAndPullSameEventTime) { - pullData.push_back(createSimpleEvent(1111L, 33)); - pullSuccess = true; - int64_t eventTimeNs = getElapsedRealtimeNs(); - // timeout is 5ms - pullDelayNs = MillisToNano(6); - - vector<std::shared_ptr<LogEvent>> dataHolder; - EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); - - // Sleep to ensure the cool down expires. 6ms is taken by the delay, so only 5 is needed here. - sleep_for(std::chrono::milliseconds(5)); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - pullDelayNs = 0; - - pullSuccess = true; - dataHolder.clear(); - EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -TEST_F(StatsPullerTest, PullFailsAndPullSameEventTime) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = false; - int64_t eventTimeNs = getElapsedRealtimeNs(); - - vector<std::shared_ptr<LogEvent>> dataHolder; - EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); - - // Sleep to ensure the cool down expires. - sleep_for(std::chrono::milliseconds(11)); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - pullSuccess = true; - - EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp deleted file mode 100644 index a21dc8717776..000000000000 --- a/cmds/statsd/tests/external/puller_util_test.cpp +++ /dev/null @@ -1,408 +0,0 @@ -// 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. - -#include "external/puller_util.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <stdio.h> - -#include <vector> - -#include "../metrics/metrics_test_helper.h" -#include "FieldValue.h" -#include "annotations.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -using namespace testing; -using std::shared_ptr; -using std::vector; -/* - * Test merge isolated and host uid - */ -namespace { -const int uidAtomTagId = 100; -const vector<int> additiveFields = {3}; -const int nonUidAtomTagId = 200; -const int timestamp = 1234; -const int isolatedUid1 = 30; -const int isolatedUid2 = 40; -const int isolatedNonAdditiveData = 32; -const int isolatedAdditiveData = 31; -const int hostUid = 20; -const int hostNonAdditiveData = 22; -const int hostAdditiveData = 21; -const int attributionAtomTagId = 300; - -sp<MockUidMap> makeMockUidMap() { - return makeMockUidMapForOneHost(hostUid, {isolatedUid1, isolatedUid2}); -} - -} // anonymous namespace - -TEST(PullerUtilTest, MergeNoDimension) { - vector<shared_ptr<LogEvent>> data = { - // 30->22->31 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData, - isolatedAdditiveData), - - // 20->22->21 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData, - hostAdditiveData), - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - - ASSERT_EQ(1, (int)data.size()); - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(2).mValue.int_value); -} - -TEST(PullerUtilTest, MergeWithDimension) { - vector<shared_ptr<LogEvent>> data = { - // 30->32->31 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 20->32->21 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData, - hostAdditiveData), - - // 20->22->21 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData, - hostAdditiveData), - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value); - - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value); -} - -TEST(PullerUtilTest, NoMergeHostUidOnly) { - vector<shared_ptr<LogEvent>> data = { - // 20->32->31 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 20->22->21 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData, - hostAdditiveData), - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value); - - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value); -} - -TEST(PullerUtilTest, IsolatedUidOnly) { - vector<shared_ptr<LogEvent>> data = { - // 30->32->31 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 30->22->21 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData, - hostAdditiveData), - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - // 20->32->31 - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value); - - // 20->22->21 - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value); -} - -TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUid) { - vector<shared_ptr<LogEvent>> data = { - // 30->32->31 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 31->32->21 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid2, isolatedNonAdditiveData, - hostAdditiveData), - - // 20->32->21 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData, - hostAdditiveData), - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - - ASSERT_EQ(1, (int)data.size()); - - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData, - actualFieldValues->at(2).mValue.int_value); -} - -TEST(PullerUtilTest, NoNeedToMerge) { - vector<shared_ptr<LogEvent>> data = { - // 32->31 - CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 22->21 - CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, hostNonAdditiveData, - hostAdditiveData), - - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, nonUidAtomTagId, {} /*no additive fields*/); - - ASSERT_EQ(2, (int)data.size()); - - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(2, actualFieldValues->size()); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(1).mValue.int_value); - - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(2, actualFieldValues->size()); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(1).mValue.int_value); -} - -TEST(PullerUtilTest, MergeNoDimensionAttributionChain) { - vector<shared_ptr<LogEvent>> data = { - // 30->tag1->400->tag2->22->31 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, - {"tag1", "tag2"}, hostNonAdditiveData, isolatedAdditiveData), - - // 20->tag1->400->tag2->22->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, - {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); - - ASSERT_EQ(1, (int)data.size()); - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(5).mValue.int_value); -} - -TEST(PullerUtilTest, MergeWithDimensionAttributionChain) { - vector<shared_ptr<LogEvent>> data = { - // 200->tag1->30->tag2->32->31 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, isolatedUid1}, - {"tag1", "tag2"}, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 200->tag1->20->tag2->32->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid}, - {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData), - - // 200->tag1->20->tag2->22->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid}, - {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value); - - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value); -} - -TEST(PullerUtilTest, NoMergeHostUidOnlyAttributionChain) { - vector<shared_ptr<LogEvent>> data = { - // 20->tag1->400->tag2->32->31 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, - {"tag1", "tag2"}, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 20->tag1->400->tag2->22->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, - {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value); - - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value); -} - -TEST(PullerUtilTest, IsolatedUidOnlyAttributionChain) { - vector<shared_ptr<LogEvent>> data = { - // 30->tag1->400->tag2->32->31 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, - {"tag1", "tag2"}, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 30->tag1->400->tag2->22->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, - {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - // 20->tag1->400->tag2->32->31 - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value); - - // 20->tag1->400->tag2->22->21 - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value); -} - -TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUidAttributionChain) { - vector<shared_ptr<LogEvent>> data = { - // 30->tag1->400->tag2->32->31 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, - {"tag1", "tag2"}, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 31->tag1->400->tag2->32->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid2, 400}, - {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData), - - // 20->tag1->400->tag2->32->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, - {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData), - }; - - sp<MockUidMap> uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); - - ASSERT_EQ(1, (int)data.size()); - - const vector<FieldValue>* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData, - actualFieldValues->at(5).mValue.int_value); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp deleted file mode 100644 index 428c46f8a0d2..000000000000 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ /dev/null @@ -1,544 +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. - -#include "src/guardrail/StatsdStats.h" -#include "statslog_statsdtest.h" -#include "tests/statsd_test_util.h" - -#include <gtest/gtest.h> -#include <vector> - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -TEST(StatsdStatsTest, TestValidConfigAdd) { - StatsdStats stats; - ConfigKey key(0, 12345); - const int metricsCount = 10; - const int conditionsCount = 20; - const int matchersCount = 30; - const int alertsCount = 10; - stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, - true /*valid config*/); - vector<uint8_t> output; - stats.dumpStats(&output, false /*reset stats*/); - - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport = report.config_stats(0); - EXPECT_EQ(0, configReport.uid()); - EXPECT_EQ(12345, configReport.id()); - EXPECT_EQ(metricsCount, configReport.metric_count()); - EXPECT_EQ(conditionsCount, configReport.condition_count()); - EXPECT_EQ(matchersCount, configReport.matcher_count()); - EXPECT_EQ(alertsCount, configReport.alert_count()); - EXPECT_EQ(true, configReport.is_valid()); - EXPECT_FALSE(configReport.has_deletion_time_sec()); -} - -TEST(StatsdStatsTest, TestInvalidConfigAdd) { - StatsdStats stats; - ConfigKey key(0, 12345); - const int metricsCount = 10; - const int conditionsCount = 20; - const int matchersCount = 30; - const int alertsCount = 10; - stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, - false /*bad config*/); - vector<uint8_t> output; - stats.dumpStats(&output, false); - - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport = report.config_stats(0); - // The invalid config should be put into icebox with a deletion time. - EXPECT_TRUE(configReport.has_deletion_time_sec()); -} - -TEST(StatsdStatsTest, TestConfigRemove) { - StatsdStats stats; - ConfigKey key(0, 12345); - const int metricsCount = 10; - const int conditionsCount = 20; - const int matchersCount = 30; - const int alertsCount = 10; - stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, - true); - vector<uint8_t> output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport = report.config_stats(0); - EXPECT_FALSE(configReport.has_deletion_time_sec()); - - stats.noteConfigRemoved(key); - stats.dumpStats(&output, false); - good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport2 = report.config_stats(0); - EXPECT_TRUE(configReport2.has_deletion_time_sec()); -} - -TEST(StatsdStatsTest, TestSubStats) { - StatsdStats stats; - ConfigKey key(0, 12345); - stats.noteConfigReceived(key, 2, 3, 4, 5, {std::make_pair(123, 456)}, true); - - stats.noteMatcherMatched(key, StringToId("matcher1")); - stats.noteMatcherMatched(key, StringToId("matcher1")); - stats.noteMatcherMatched(key, StringToId("matcher2")); - - stats.noteConditionDimensionSize(key, StringToId("condition1"), 250); - stats.noteConditionDimensionSize(key, StringToId("condition1"), 240); - - stats.noteMetricDimensionSize(key, StringToId("metric1"), 201); - stats.noteMetricDimensionSize(key, StringToId("metric1"), 202); - - stats.noteAnomalyDeclared(key, StringToId("alert1")); - stats.noteAnomalyDeclared(key, StringToId("alert1")); - stats.noteAnomalyDeclared(key, StringToId("alert2")); - - // broadcast-> 2 - stats.noteBroadcastSent(key); - stats.noteBroadcastSent(key); - - // data drop -> 1 - stats.noteDataDropped(key, 123); - - // dump report -> 3 - stats.noteMetricsReportSent(key, 0); - stats.noteMetricsReportSent(key, 0); - stats.noteMetricsReportSent(key, 0); - - // activation_time_sec -> 2 - stats.noteActiveStatusChanged(key, true); - stats.noteActiveStatusChanged(key, true); - - // deactivation_time_sec -> 1 - stats.noteActiveStatusChanged(key, false); - - vector<uint8_t> output; - stats.dumpStats(&output, true); // Dump and reset stats - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport = report.config_stats(0); - ASSERT_EQ(2, configReport.broadcast_sent_time_sec_size()); - ASSERT_EQ(1, configReport.data_drop_time_sec_size()); - ASSERT_EQ(1, configReport.data_drop_bytes_size()); - EXPECT_EQ(123, configReport.data_drop_bytes(0)); - ASSERT_EQ(3, configReport.dump_report_time_sec_size()); - ASSERT_EQ(3, configReport.dump_report_data_size_size()); - ASSERT_EQ(2, configReport.activation_time_sec_size()); - ASSERT_EQ(1, configReport.deactivation_time_sec_size()); - ASSERT_EQ(1, configReport.annotation_size()); - EXPECT_EQ(123, configReport.annotation(0).field_int64()); - EXPECT_EQ(456, configReport.annotation(0).field_int32()); - - ASSERT_EQ(2, configReport.matcher_stats_size()); - // matcher1 is the first in the list - if (configReport.matcher_stats(0).id() == StringToId("matcher1")) { - EXPECT_EQ(2, configReport.matcher_stats(0).matched_times()); - EXPECT_EQ(1, configReport.matcher_stats(1).matched_times()); - EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(1).id()); - } else { - // matcher1 is the second in the list. - EXPECT_EQ(1, configReport.matcher_stats(0).matched_times()); - EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(0).id()); - - EXPECT_EQ(2, configReport.matcher_stats(1).matched_times()); - EXPECT_EQ(StringToId("matcher1"), configReport.matcher_stats(1).id()); - } - - ASSERT_EQ(2, configReport.alert_stats_size()); - bool alert1first = configReport.alert_stats(0).id() == StringToId("alert1"); - EXPECT_EQ(StringToId("alert1"), configReport.alert_stats(alert1first ? 0 : 1).id()); - EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).alerted_times()); - EXPECT_EQ(StringToId("alert2"), configReport.alert_stats(alert1first ? 1 : 0).id()); - EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).alerted_times()); - - ASSERT_EQ(1, configReport.condition_stats_size()); - EXPECT_EQ(StringToId("condition1"), configReport.condition_stats(0).id()); - EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts()); - - ASSERT_EQ(1, configReport.metric_stats_size()); - EXPECT_EQ(StringToId("metric1"), configReport.metric_stats(0).id()); - EXPECT_EQ(202, configReport.metric_stats(0).max_tuple_counts()); - - // after resetting the stats, some new events come - stats.noteMatcherMatched(key, StringToId("matcher99")); - stats.noteConditionDimensionSize(key, StringToId("condition99"), 300); - stats.noteMetricDimensionSize(key, StringToId("metric99tion99"), 270); - stats.noteAnomalyDeclared(key, StringToId("alert99")); - - // now the config stats should only contain the stats about the new event. - stats.dumpStats(&output, false); - good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport2 = report.config_stats(0); - ASSERT_EQ(1, configReport2.matcher_stats_size()); - EXPECT_EQ(StringToId("matcher99"), configReport2.matcher_stats(0).id()); - EXPECT_EQ(1, configReport2.matcher_stats(0).matched_times()); - - ASSERT_EQ(1, configReport2.condition_stats_size()); - EXPECT_EQ(StringToId("condition99"), configReport2.condition_stats(0).id()); - EXPECT_EQ(300, configReport2.condition_stats(0).max_tuple_counts()); - - ASSERT_EQ(1, configReport2.metric_stats_size()); - EXPECT_EQ(StringToId("metric99tion99"), configReport2.metric_stats(0).id()); - EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts()); - - ASSERT_EQ(1, configReport2.alert_stats_size()); - EXPECT_EQ(StringToId("alert99"), configReport2.alert_stats(0).id()); - EXPECT_EQ(1, configReport2.alert_stats(0).alerted_times()); -} - -TEST(StatsdStatsTest, TestAtomLog) { - StatsdStats stats; - time_t now = time(nullptr); - // old event, we get it from the stats buffer. should be ignored. - stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, 1000); - - stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, now + 1); - stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, now + 2); - stats.noteAtomLogged(util::APP_CRASH_OCCURRED, now + 3); - - vector<uint8_t> output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - - ASSERT_EQ(2, report.atom_stats_size()); - bool sensorAtomGood = false; - bool dropboxAtomGood = false; - - for (const auto& atomStats : report.atom_stats()) { - if (atomStats.tag() == util::SENSOR_STATE_CHANGED && atomStats.count() == 3) { - sensorAtomGood = true; - } - if (atomStats.tag() == util::APP_CRASH_OCCURRED && atomStats.count() == 1) { - dropboxAtomGood = true; - } - } - - EXPECT_TRUE(dropboxAtomGood); - EXPECT_TRUE(sensorAtomGood); -} - -TEST(StatsdStatsTest, TestNonPlatformAtomLog) { - StatsdStats stats; - time_t now = time(nullptr); - int newAtom1 = StatsdStats::kMaxPushedAtomId + 1; - int newAtom2 = StatsdStats::kMaxPushedAtomId + 2; - - stats.noteAtomLogged(newAtom1, now + 1); - stats.noteAtomLogged(newAtom1, now + 2); - stats.noteAtomLogged(newAtom2, now + 3); - - vector<uint8_t> output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - - ASSERT_EQ(2, report.atom_stats_size()); - bool newAtom1Good = false; - bool newAtom2Good = false; - - for (const auto& atomStats : report.atom_stats()) { - if (atomStats.tag() == newAtom1 && atomStats.count() == 2) { - newAtom1Good = true; - } - if (atomStats.tag() == newAtom2 && atomStats.count() == 1) { - newAtom2Good = true; - } - } - - EXPECT_TRUE(newAtom1Good); - EXPECT_TRUE(newAtom2Good); -} - -TEST(StatsdStatsTest, TestPullAtomStats) { - StatsdStats stats; - - stats.updateMinPullIntervalSec(util::DISK_SPACE, 3333L); - stats.updateMinPullIntervalSec(util::DISK_SPACE, 2222L); - stats.updateMinPullIntervalSec(util::DISK_SPACE, 4444L); - - stats.notePull(util::DISK_SPACE); - stats.notePullTime(util::DISK_SPACE, 1111L); - stats.notePullDelay(util::DISK_SPACE, 1111L); - stats.notePull(util::DISK_SPACE); - stats.notePullTime(util::DISK_SPACE, 3333L); - stats.notePullDelay(util::DISK_SPACE, 3335L); - stats.notePull(util::DISK_SPACE); - stats.notePullFromCache(util::DISK_SPACE); - stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true); - stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, false); - stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true); - stats.notePullBinderCallFailed(util::DISK_SPACE); - stats.notePullUidProviderNotFound(util::DISK_SPACE); - stats.notePullerNotFound(util::DISK_SPACE); - stats.notePullerNotFound(util::DISK_SPACE); - stats.notePullTimeout(util::DISK_SPACE, 3000L, 6000L); - stats.notePullTimeout(util::DISK_SPACE, 4000L, 7000L); - - vector<uint8_t> output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - - ASSERT_EQ(1, report.pulled_atom_stats_size()); - - EXPECT_EQ(util::DISK_SPACE, report.pulled_atom_stats(0).atom_id()); - EXPECT_EQ(3, report.pulled_atom_stats(0).total_pull()); - EXPECT_EQ(1, report.pulled_atom_stats(0).total_pull_from_cache()); - EXPECT_EQ(2222L, report.pulled_atom_stats(0).min_pull_interval_sec()); - EXPECT_EQ(2222L, report.pulled_atom_stats(0).average_pull_time_nanos()); - EXPECT_EQ(3333L, report.pulled_atom_stats(0).max_pull_time_nanos()); - EXPECT_EQ(2223L, report.pulled_atom_stats(0).average_pull_delay_nanos()); - EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos()); - EXPECT_EQ(2L, report.pulled_atom_stats(0).registered_count()); - EXPECT_EQ(1L, report.pulled_atom_stats(0).unregistered_count()); - EXPECT_EQ(1L, report.pulled_atom_stats(0).binder_call_failed()); - EXPECT_EQ(1L, report.pulled_atom_stats(0).failed_uid_provider_not_found()); - EXPECT_EQ(2L, report.pulled_atom_stats(0).puller_not_found()); - ASSERT_EQ(2, report.pulled_atom_stats(0).pull_atom_metadata_size()); - EXPECT_EQ(3000L, report.pulled_atom_stats(0).pull_atom_metadata(0).pull_timeout_uptime_millis()); - EXPECT_EQ(4000L, report.pulled_atom_stats(0).pull_atom_metadata(1).pull_timeout_uptime_millis()); - EXPECT_EQ(6000L, report.pulled_atom_stats(0).pull_atom_metadata(0) - .pull_timeout_elapsed_millis()); - EXPECT_EQ(7000L, report.pulled_atom_stats(0).pull_atom_metadata(1) - .pull_timeout_elapsed_millis()); -} - -TEST(StatsdStatsTest, TestAtomMetricsStats) { - StatsdStats stats; - time_t now = time(nullptr); - // old event, we get it from the stats buffer. should be ignored. - stats.noteBucketDropped(1000L); - - stats.noteBucketBoundaryDelayNs(1000L, -1L); - stats.noteBucketBoundaryDelayNs(1000L, -10L); - stats.noteBucketBoundaryDelayNs(1000L, 2L); - - stats.noteBucketBoundaryDelayNs(1001L, 1L); - - vector<uint8_t> output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - - ASSERT_EQ(2, report.atom_metric_stats().size()); - - auto atomStats = report.atom_metric_stats(0); - EXPECT_EQ(1000L, atomStats.metric_id()); - EXPECT_EQ(1L, atomStats.bucket_dropped()); - EXPECT_EQ(-10L, atomStats.min_bucket_boundary_delay_ns()); - EXPECT_EQ(2L, atomStats.max_bucket_boundary_delay_ns()); - - auto atomStats2 = report.atom_metric_stats(1); - EXPECT_EQ(1001L, atomStats2.metric_id()); - EXPECT_EQ(0L, atomStats2.bucket_dropped()); - EXPECT_EQ(0L, atomStats2.min_bucket_boundary_delay_ns()); - EXPECT_EQ(1L, atomStats2.max_bucket_boundary_delay_ns()); -} - -TEST(StatsdStatsTest, TestAnomalyMonitor) { - StatsdStats stats; - stats.noteRegisteredAnomalyAlarmChanged(); - stats.noteRegisteredAnomalyAlarmChanged(); - - vector<uint8_t> output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - - EXPECT_EQ(2, report.anomaly_alarm_stats().alarms_registered()); -} - -TEST(StatsdStatsTest, TestTimestampThreshold) { - StatsdStats stats; - vector<int32_t> timestamps; - for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) { - timestamps.push_back(i); - } - ConfigKey key(0, 12345); - stats.noteConfigReceived(key, 2, 3, 4, 5, {}, true); - - for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) { - stats.noteDataDropped(key, timestamps[i]); - stats.noteBroadcastSent(key, timestamps[i]); - stats.noteMetricsReportSent(key, 0, timestamps[i]); - stats.noteActiveStatusChanged(key, true, timestamps[i]); - stats.noteActiveStatusChanged(key, false, timestamps[i]); - } - - int32_t newTimestamp = 10000; - - // now it should trigger removing oldest timestamp - stats.noteDataDropped(key, 123, 10000); - stats.noteBroadcastSent(key, 10000); - stats.noteMetricsReportSent(key, 0, 10000); - stats.noteActiveStatusChanged(key, true, 10000); - stats.noteActiveStatusChanged(key, false, 10000); - - EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end()); - const auto& configStats = stats.mConfigStats[key]; - - size_t maxCount = StatsdStats::kMaxTimestampCount; - ASSERT_EQ(maxCount, configStats->broadcast_sent_time_sec.size()); - ASSERT_EQ(maxCount, configStats->data_drop_time_sec.size()); - ASSERT_EQ(maxCount, configStats->dump_report_stats.size()); - ASSERT_EQ(maxCount, configStats->activation_time_sec.size()); - ASSERT_EQ(maxCount, configStats->deactivation_time_sec.size()); - - // the oldest timestamp is the second timestamp in history - EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); - EXPECT_EQ(1, configStats->data_drop_bytes.front()); - EXPECT_EQ(1, configStats->dump_report_stats.front().first); - EXPECT_EQ(1, configStats->activation_time_sec.front()); - EXPECT_EQ(1, configStats->deactivation_time_sec.front()); - - // the last timestamp is the newest timestamp. - EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back()); - EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back()); - EXPECT_EQ(123, configStats->data_drop_bytes.back()); - EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first); - EXPECT_EQ(newTimestamp, configStats->activation_time_sec.back()); - EXPECT_EQ(newTimestamp, configStats->deactivation_time_sec.back()); -} - -TEST(StatsdStatsTest, TestSystemServerCrash) { - StatsdStats stats; - vector<int32_t> timestamps; - for (int i = 0; i < StatsdStats::kMaxSystemServerRestarts; i++) { - timestamps.push_back(i); - stats.noteSystemServerRestart(timestamps[i]); - } - vector<uint8_t> output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - EXPECT_TRUE(report.ParseFromArray(&output[0], output.size())); - const int maxCount = StatsdStats::kMaxSystemServerRestarts; - ASSERT_EQ(maxCount, (int)report.system_restart_sec_size()); - - stats.noteSystemServerRestart(StatsdStats::kMaxSystemServerRestarts + 1); - output.clear(); - stats.dumpStats(&output, false); - EXPECT_TRUE(report.ParseFromArray(&output[0], output.size())); - ASSERT_EQ(maxCount, (int)report.system_restart_sec_size()); - EXPECT_EQ(StatsdStats::kMaxSystemServerRestarts + 1, report.system_restart_sec(maxCount - 1)); -} - -TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit) { - StatsdStats stats; - int uid1 = 1; - int uid2 = 2; - stats.noteActivationBroadcastGuardrailHit(uid1, 10); - stats.noteActivationBroadcastGuardrailHit(uid1, 20); - - // Test that we only keep 20 timestamps. - for (int i = 0; i < 100; i++) { - stats.noteActivationBroadcastGuardrailHit(uid2, i); - } - - vector<uint8_t> output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - EXPECT_TRUE(report.ParseFromArray(&output[0], output.size())); - - ASSERT_EQ(2, report.activation_guardrail_stats_size()); - bool uid1Good = false; - bool uid2Good = false; - for (const auto& guardrailTimes : report.activation_guardrail_stats()) { - if (uid1 == guardrailTimes.uid()) { - uid1Good = true; - ASSERT_EQ(2, guardrailTimes.guardrail_met_sec_size()); - EXPECT_EQ(10, guardrailTimes.guardrail_met_sec(0)); - EXPECT_EQ(20, guardrailTimes.guardrail_met_sec(1)); - } else if (uid2 == guardrailTimes.uid()) { - int maxCount = StatsdStats::kMaxTimestampCount; - uid2Good = true; - ASSERT_EQ(maxCount, guardrailTimes.guardrail_met_sec_size()); - for (int i = 0; i < maxCount; i++) { - EXPECT_EQ(100 - maxCount + i, guardrailTimes.guardrail_met_sec(i)); - } - } else { - FAIL() << "Unexpected uid."; - } - } - EXPECT_TRUE(uid1Good); - EXPECT_TRUE(uid2Good); -} - -TEST(StatsdStatsTest, TestAtomErrorStats) { - StatsdStats stats; - - int pushAtomTag = 100; - int pullAtomTag = 1000; - int numErrors = 10; - - for (int i = 0; i < numErrors; i++) { - // We must call noteAtomLogged as well because only those pushed atoms - // that have been logged will have stats printed about them in the - // proto. - stats.noteAtomLogged(pushAtomTag, /*timeSec=*/0); - stats.noteAtomError(pushAtomTag, /*pull=*/false); - - stats.noteAtomError(pullAtomTag, /*pull=*/true); - } - - vector<uint8_t> output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - EXPECT_TRUE(report.ParseFromArray(&output[0], output.size())); - - // Check error count = numErrors for push atom - ASSERT_EQ(1, report.atom_stats_size()); - const auto& pushedAtomStats = report.atom_stats(0); - EXPECT_EQ(pushAtomTag, pushedAtomStats.tag()); - EXPECT_EQ(numErrors, pushedAtomStats.error_count()); - - // Check error count = numErrors for pull atom - ASSERT_EQ(1, report.pulled_atom_stats_size()); - const auto& pulledAtomStats = report.pulled_atom_stats(0); - EXPECT_EQ(pullAtomTag, pulledAtomStats.atom_id()); - EXPECT_EQ(numErrors, pulledAtomStats.atom_error_count()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp deleted file mode 100644 index 3a654565b4aa..000000000000 --- a/cmds/statsd/tests/indexed_priority_queue_test.cpp +++ /dev/null @@ -1,235 +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. - */ - -#include "src/anomaly/indexed_priority_queue.h" - -#include <gtest/gtest.h> - -using namespace android::os::statsd; - -/** struct for template in indexed_priority_queue */ -struct AATest : public RefBase { - AATest(uint32_t val, std::string a, std::string b) : val(val), a(a), b(b) { - } - - const int val; - const std::string a; - const std::string b; - - struct Smaller { - bool operator()(const sp<const AATest> a, const sp<const AATest> b) const { - return (a->val < b->val); - } - }; -}; - -#ifdef __ANDROID__ -TEST(indexed_priority_queue, empty_and_size) { - std::string emptyMetricId; - std::string emptyDimensionId; - indexed_priority_queue<AATest, AATest::Smaller> ipq; - sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId}; - sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId}; - - ASSERT_EQ(0u, ipq.size()); - EXPECT_TRUE(ipq.empty()); - - ipq.push(aa4); - ASSERT_EQ(1u, ipq.size()); - EXPECT_FALSE(ipq.empty()); - - ipq.push(aa8); - ASSERT_EQ(2u, ipq.size()); - EXPECT_FALSE(ipq.empty()); - - ipq.remove(aa4); - ASSERT_EQ(1u, ipq.size()); - EXPECT_FALSE(ipq.empty()); - - ipq.remove(aa8); - ASSERT_EQ(0u, ipq.size()); - EXPECT_TRUE(ipq.empty()); -} - -TEST(indexed_priority_queue, top) { - std::string emptyMetricId; - std::string emptyDimensionId; - indexed_priority_queue<AATest, AATest::Smaller> ipq; - sp<const AATest> aa2 = new AATest{2, emptyMetricId, emptyDimensionId}; - sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId}; - sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId}; - sp<const AATest> aa12 = new AATest{12, emptyMetricId, emptyDimensionId}; - sp<const AATest> aa16 = new AATest{16, emptyMetricId, emptyDimensionId}; - sp<const AATest> aa20 = new AATest{20, emptyMetricId, emptyDimensionId}; - - EXPECT_EQ(ipq.top(), nullptr); - - // add 8, 4, 12 - ipq.push(aa8); - EXPECT_EQ(ipq.top(), aa8); - - ipq.push(aa12); - EXPECT_EQ(ipq.top(), aa8); - - ipq.push(aa4); - EXPECT_EQ(ipq.top(), aa4); - - // remove 12, 4 - ipq.remove(aa12); - EXPECT_EQ(ipq.top(), aa4); - - ipq.remove(aa4); - EXPECT_EQ(ipq.top(), aa8); - - // add 16, 2, 20 - ipq.push(aa16); - EXPECT_EQ(ipq.top(), aa8); - - ipq.push(aa2); - EXPECT_EQ(ipq.top(), aa2); - - ipq.push(aa20); - EXPECT_EQ(ipq.top(), aa2); - - // remove 2, 20, 16, 8 - ipq.remove(aa2); - EXPECT_EQ(ipq.top(), aa8); - - ipq.remove(aa20); - EXPECT_EQ(ipq.top(), aa8); - - ipq.remove(aa16); - EXPECT_EQ(ipq.top(), aa8); - - ipq.remove(aa8); - EXPECT_EQ(ipq.top(), nullptr); -} - -TEST(indexed_priority_queue, push_same_aa) { - std::string emptyMetricId; - std::string emptyDimensionId; - indexed_priority_queue<AATest, AATest::Smaller> ipq; - sp<const AATest> aa4_a = new AATest{4, emptyMetricId, emptyDimensionId}; - sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId}; - - ipq.push(aa4_a); - ASSERT_EQ(1u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4_a)); - EXPECT_FALSE(ipq.contains(aa4_b)); - - ipq.push(aa4_a); - ASSERT_EQ(1u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4_a)); - EXPECT_FALSE(ipq.contains(aa4_b)); - - ipq.push(aa4_b); - ASSERT_EQ(2u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4_a)); - EXPECT_TRUE(ipq.contains(aa4_b)); -} - -TEST(indexed_priority_queue, remove_nonexistant) { - std::string emptyMetricId; - std::string emptyDimensionId; - indexed_priority_queue<AATest, AATest::Smaller> ipq; - sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId}; - sp<const AATest> aa5 = new AATest{5, emptyMetricId, emptyDimensionId}; - - ipq.push(aa4); - ipq.remove(aa5); - ASSERT_EQ(1u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4)); - EXPECT_FALSE(ipq.contains(aa5)); -} - -TEST(indexed_priority_queue, remove_same_aa) { - indexed_priority_queue<AATest, AATest::Smaller> ipq; - std::string emptyMetricId; - std::string emptyDimensionId; - sp<const AATest> aa4_a = new AATest{4, emptyMetricId, emptyDimensionId}; - sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId}; - - ipq.push(aa4_a); - ipq.push(aa4_b); - ASSERT_EQ(2u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4_a)); - EXPECT_TRUE(ipq.contains(aa4_b)); - - ipq.remove(aa4_b); - ASSERT_EQ(1u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4_a)); - EXPECT_FALSE(ipq.contains(aa4_b)); - - ipq.remove(aa4_a); - ASSERT_EQ(0u, ipq.size()); - EXPECT_FALSE(ipq.contains(aa4_a)); - EXPECT_FALSE(ipq.contains(aa4_b)); -} - -TEST(indexed_priority_queue, nulls) { - indexed_priority_queue<AATest, AATest::Smaller> ipq; - - EXPECT_TRUE(ipq.empty()); - EXPECT_FALSE(ipq.contains(nullptr)); - - ipq.push(nullptr); - EXPECT_TRUE(ipq.empty()); - EXPECT_FALSE(ipq.contains(nullptr)); - - ipq.remove(nullptr); - EXPECT_TRUE(ipq.empty()); - EXPECT_FALSE(ipq.contains(nullptr)); -} - -TEST(indexed_priority_queue, pop) { - indexed_priority_queue<AATest, AATest::Smaller> ipq; - std::string emptyMetricId; - std::string emptyDimensionId; - sp<const AATest> a = new AATest{1, emptyMetricId, emptyDimensionId}; - sp<const AATest> b = new AATest{2, emptyMetricId, emptyDimensionId}; - sp<const AATest> c = new AATest{3, emptyMetricId, emptyDimensionId}; - - ipq.push(c); - ipq.push(b); - ipq.push(a); - ASSERT_EQ(3u, ipq.size()); - - ipq.pop(); - ASSERT_EQ(2u, ipq.size()); - EXPECT_FALSE(ipq.contains(a)); - EXPECT_TRUE(ipq.contains(b)); - EXPECT_TRUE(ipq.contains(c)); - - ipq.pop(); - ASSERT_EQ(1u, ipq.size()); - EXPECT_FALSE(ipq.contains(a)); - EXPECT_FALSE(ipq.contains(b)); - EXPECT_TRUE(ipq.contains(c)); - - ipq.pop(); - ASSERT_EQ(0u, ipq.size()); - EXPECT_FALSE(ipq.contains(a)); - EXPECT_FALSE(ipq.contains(b)); - EXPECT_FALSE(ipq.contains(c)); - EXPECT_TRUE(ipq.empty()); - - ipq.pop(); // pop an empty queue - EXPECT_TRUE(ipq.empty()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp deleted file mode 100644 index a15f95bef358..000000000000 --- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "logd/LogEventQueue.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <stdio.h> - -#include <thread> - -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -namespace android { -namespace os { -namespace statsd { - -using namespace android; -using namespace testing; - -using std::unique_ptr; - -namespace { - -std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 10); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -} // anonymous namespace - -#ifdef __ANDROID__ -TEST(LogEventQueue_test, TestGoodConsumer) { - LogEventQueue queue(50); - int64_t timeBaseNs = 100; - std::thread writer([&queue, timeBaseNs] { - for (int i = 0; i < 100; i++) { - int64_t oldestEventNs; - bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs); - EXPECT_TRUE(success); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - }); - - std::thread reader([&queue, timeBaseNs] { - for (int i = 0; i < 100; i++) { - auto event = queue.waitPop(); - EXPECT_TRUE(event != nullptr); - // All events are in right order. - EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs()); - } - }); - - reader.join(); - writer.join(); -} - -TEST(LogEventQueue_test, TestSlowConsumer) { - LogEventQueue queue(50); - int64_t timeBaseNs = 100; - std::thread writer([&queue, timeBaseNs] { - int failure_count = 0; - int64_t oldestEventNs; - for (int i = 0; i < 100; i++) { - bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs); - if (!success) failure_count++; - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - - // There is some remote chance that reader thread not get chance to run before writer thread - // ends. That's why the following comparison is not "==". - // There will be at least 45 events lost due to overflow. - EXPECT_TRUE(failure_count >= 45); - // The oldest event must be at least the 6th event. - EXPECT_TRUE(oldestEventNs <= (100 + 5 * 1000)); - }); - - std::thread reader([&queue, timeBaseNs] { - // The consumer quickly processed 5 events, then it got stuck (not reading anymore). - for (int i = 0; i < 5; i++) { - auto event = queue.waitPop(); - EXPECT_TRUE(event != nullptr); - // All events are in right order. - EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs()); - } - }); - - reader.join(); - writer.join(); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/metadata_util_test.cpp b/cmds/statsd/tests/metadata_util_test.cpp deleted file mode 100644 index 7707890cbd0c..000000000000 --- a/cmds/statsd/tests/metadata_util_test.cpp +++ /dev/null @@ -1,69 +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. - */ -#include <gtest/gtest.h> - -#include "metadata_util.h" -#include "tests/statsd_test_util.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -TEST(MetadataUtilTest, TestWriteAndReadMetricDimensionKey) { - HashableDimensionKey dim; - HashableDimensionKey dim2; - int pos1[] = {1, 1, 1}; - int pos2[] = {1, 1, 2}; - int pos3[] = {1, 1, 3}; - int pos4[] = {2, 0, 0}; - Field field1(10, pos1, 2); - Field field2(10, pos2, 2); - Field field3(10, pos3, 2); - Field field4(10, pos4, 0); - - Value value1((int32_t)10025); - Value value2("tag"); - Value value3((int32_t)987654); - Value value4((int32_t)99999); - - dim.addValue(FieldValue(field1, value1)); - dim.addValue(FieldValue(field2, value2)); - dim.addValue(FieldValue(field3, value3)); - dim.addValue(FieldValue(field4, value4)); - - dim2.addValue(FieldValue(field1, value1)); - dim2.addValue(FieldValue(field2, value2)); - - MetricDimensionKey dimKey(dim, dim2); - - metadata::MetricDimensionKey metadataDimKey; - writeMetricDimensionKeyToMetadataDimensionKey(dimKey, &metadataDimKey); - - MetricDimensionKey loadedDimKey = loadMetricDimensionKeyFromProto(metadataDimKey); - - ASSERT_EQ(loadedDimKey, dimKey); - ASSERT_EQ(std::hash<MetricDimensionKey>{}(loadedDimKey), - std::hash<MetricDimensionKey>{}(dimKey)); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp deleted file mode 100644 index 8e2864c6fba8..000000000000 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ /dev/null @@ -1,477 +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. - -#include "src/metrics/CountMetricProducer.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; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - - -namespace { -const ConfigKey kConfigKey(0, 12345); -const uint64_t protoHash = 0x1234567890; - -void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -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()); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -} // namespace - -// Setup for parameterized tests. -class CountMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {}; - -INSTANTIATE_TEST_SUITE_P(CountMetricProducerTest_PartialBucket, - CountMetricProducerTest_PartialBucket, - testing::Values(APP_UPGRADE, BOOT_COMPLETE)); - -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, protoHash, 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, protoHash, 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); - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - - // Flushes. - countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); - ASSERT_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]; - ASSERT_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); - ASSERT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != - countProducer.mPastBuckets.end()); - ASSERT_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); - ASSERT_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]; - ASSERT_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, 0, {ConditionState::kUnknown}, wizard, - protoHash, bucketStartTimeNs, bucketStartTimeNs); - - countProducer.onConditionChanged(true, bucketStartTimeNs); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1); - countProducer.onMatchedLogEvent(1 /*matcher index*/, event1); - - ASSERT_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); - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - - countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); - ASSERT_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]; - ASSERT_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, 0 /*condition tracker index*/, - {ConditionState::kUnknown}, wizard, protoHash, - bucketStartTimeNs, bucketStartTimeNs); - - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); - ASSERT_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]; - ASSERT_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_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) { - sp<AlarmMonitor> alarmMonitor; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventTimeNs = 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, - protoHash, bucketStartTimeNs, bucketStartTimeNs); - - sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); - EXPECT_TRUE(anomalyTracker != nullptr); - - // Bucket is not flushed yet. - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - - // App upgrade or boot complete forces bucket flush. - // Check that there's a past bucket and the bucket end is not adjusted. - switch (GetParam()) { - case APP_UPGRADE: - countProducer.notifyAppUpgrade(eventTimeNs); - break; - case BOOT_COMPLETE: - countProducer.onStatsdInitCompleted(eventTimeNs); - break; - } - ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(eventTimeNs, - countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ(0, countProducer.getCurrentBucketNum()); - EXPECT_EQ(eventTimeNs, 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); - ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(0, countProducer.getCurrentBucketNum()); - 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); - ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, countProducer.getCurrentBucketNum()); - EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -} - -TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventTimeNs = 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, - protoHash, 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); - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - - // App upgrade or boot complete forces bucket flush. - // Check that there's a past bucket and the bucket end is not adjusted since the upgrade - // occurred after the bucket end time. - switch (GetParam()) { - case APP_UPGRADE: - countProducer.notifyAppUpgrade(eventTimeNs); - break; - case BOOT_COMPLETE: - countProducer.onStatsdInitCompleted(eventTimeNs); - break; - } - ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, - countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ(eventTimeNs, 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); - ASSERT_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); - ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ((int64_t)eventTimeNs, - 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, protoHash, 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); - - ASSERT_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); - ASSERT_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); - ASSERT_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); - ASSERT_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; - metric.set_id(1); - metric.set_bucket(ONE_WEEK); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - int64_t oneDayNs = 24 * 60 * 60 * 1e9; - int64_t fiveWeeksNs = 5 * 7 * oneDayNs; - - CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard, - protoHash, oneDayNs, fiveWeeksNs); - - int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs; - - EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(4, countProducer.mCurrentBucketNum); - EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp deleted file mode 100644 index d1f89775ed6a..000000000000 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ /dev/null @@ -1,516 +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. - -#include "src/metrics/DurationMetricProducer.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" -#include "tests/statsd_test_util.h" - -using namespace android::os::statsd; -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - - -namespace { - -const ConfigKey kConfigKey(0, 12345); -const uint64_t protoHash = 0x1234567890; -void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -} // namespace - -// Setup for parameterized tests. -class DurationMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {}; - -INSTANTIATE_TEST_SUITE_P(DurationMetricProducerTest_PartialBucket, - DurationMetricProducerTest_PartialBucket, - testing::Values(APP_UPGRADE, BOOT_COMPLETE)); - -TEST(DurationMetricTrackerTest, TestFirstBucket) { - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - DurationMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_SUM); - - FieldMatcher dimensions; - - DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, 5, - 600 * NS_PER_SEC + NS_PER_SEC / 2); - - EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(10, durationProducer.mCurrentBucketNum); - EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs()); -} - -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, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); - - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); - ASSERT_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]; - ASSERT_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 */, {ConditionState::kUnknown}, - 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, protoHash, 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); - ASSERT_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); - ASSERT_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]; - ASSERT_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 */, {ConditionState::kUnknown}, - 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, protoHash, 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); - ASSERT_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); - ASSERT_EQ(1UL, durationProducer.mPastBuckets.size()); - const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_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_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) { - /** - * 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; - 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, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); - - int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - ASSERT_EQ(0UL, durationProducer.mPastBuckets.size()); - EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - switch (GetParam()) { - case APP_UPGRADE: - durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - ASSERT_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(partialBucketSplitTimeNs, buckets[0].mBucketEndNs); - EXPECT_EQ(partialBucketSplitTimeNs - startTimeNs, buckets[0].mDuration); - EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(0, durationProducer.getCurrentBucketNum()); - - // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(3UL, buckets.size()); - EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - partialBucketSplitTimeNs, 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_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollowingBucket) { - /** - * 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; - 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, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); - - int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - ASSERT_EQ(0UL, durationProducer.mPastBuckets.size()); - EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; - switch (GetParam()) { - case APP_UPGRADE: - durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - ASSERT_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(partialBucketSplitTimeNs, buckets[1].mBucketEndNs); - EXPECT_EQ(partialBucketSplitTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); - EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, durationProducer.getCurrentBucketNum()); - - // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(3UL, buckets.size()); - EXPECT_EQ(partialBucketSplitTimeNs, buckets[2].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - partialBucketSplitTimeNs, - buckets[2].mDuration); -} - -TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) { - sp<AlarmMonitor> alarmMonitor; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - 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, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); - - sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); - EXPECT_TRUE(anomalyTracker != nullptr); - - int64_t startTimeNs = bucketStartTimeNs + 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - switch (GetParam()) { - case APP_UPGRADE: - durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - - // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, - anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -} - -TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int tagId = 1; - - 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, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); - - int64_t startTimeNs = bucketStartTimeNs + 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - ASSERT_EQ(0UL, durationProducer.mPastBuckets.size()); - EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - switch (GetParam()) { - case APP_UPGRADE: - durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(0, durationProducer.getCurrentBucketNum()); - - // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - ASSERT_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]; - ASSERT_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_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int tagId = 1; - - 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, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); - - int64_t startTimeNs = bucketStartTimeNs + 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - ASSERT_EQ(0UL, durationProducer.mPastBuckets.size()); - EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; - switch (GetParam()) { - case APP_UPGRADE: - durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, durationProducer.getCurrentBucketNum()); - - // Stop occurs in the same partial bucket as created for the app upgrade. - int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); - - durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); - std::vector<DurationBucket> buckets = - durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(1UL, buckets.size()); - EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); - EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp deleted file mode 100644 index 4bbbd2cb36ad..000000000000 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ /dev/null @@ -1,186 +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. - -#include "src/metrics/EventMetricProducer.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <stdio.h> - -#include <vector> - -#include "metrics_test_helper.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - - -namespace { -const ConfigKey kConfigKey(0, 12345); -const uint64_t protoHash = 0x1234567890; - -void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeString(statsEvent, str.c_str()); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} -} // anonymous namespace - -TEST(EventMetricProducerTest, TestNoCondition) { - int64_t bucketStartTimeNs = 10000000000; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - - EventMetric metric; - metric.set_id(1); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 2); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, bucketStartTimeNs); - - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - - // Check dump report content. - ProtoOutputStream output; - std::set<string> strSet; - eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, - true /*erase data*/, FAST, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_event_metrics()); - ASSERT_EQ(2, report.event_metrics().data_size()); - EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos()); - EXPECT_EQ(bucketStartTimeNs + 2, report.event_metrics().data(1).elapsed_timestamp_nanos()); -} - -TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { - int64_t bucketStartTimeNs = 10000000000; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - - EventMetric metric; - metric.set_id(1); - metric.set_condition(StringToId("SCREEN_ON")); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, protoHash, - bucketStartTimeNs); - - eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs); - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); - - eventProducer.onConditionChanged(false /*condition*/, bucketStartTimeNs + 2); - - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - - // Check dump report content. - ProtoOutputStream output; - std::set<string> strSet; - eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, - true /*erase data*/, FAST, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_event_metrics()); - ASSERT_EQ(1, report.event_metrics().data_size()); - EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos()); -} - -TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - - int tagId = 1; - int conditionTagId = 2; - - EventMetric metric; - metric.set_id(1); - metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); - MetricConditionLink* link = metric.add_links(); - link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); - buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what()); - buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition()); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1, "111"); - ConditionKey key1; - key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = { - getMockedDimensionKey(conditionTagId, 2, "111")}; - - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10, "222"); - ConditionKey key2; - key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = { - getMockedDimensionKey(conditionTagId, 2, "222")}; - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - // Condition is false for first event. - EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); - // Condition is true for second event. - EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); - - EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, protoHash, - bucketStartTimeNs); - - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - - // Check dump report content. - ProtoOutputStream output; - std::set<string> strSet; - eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, - true /*erase data*/, FAST, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_event_metrics()); - ASSERT_EQ(1, report.event_metrics().data_size()); - EXPECT_EQ(bucketStartTimeNs + 10, report.event_metrics().data(0).elapsed_timestamp_nanos()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp deleted file mode 100644 index 10606810d806..000000000000 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ /dev/null @@ -1,828 +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. - -#include "src/metrics/GaugeMetricProducer.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <math.h> -#include <stdio.h> - -#include <vector> - -#include "logd/LogEvent.h" -#include "metrics_test_helper.h" -#include "src/matchers/SimpleAtomMatchingTracker.h" -#include "src/metrics/MetricProducer.h" -#include "src/stats_log_util.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; -using std::make_shared; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -namespace { - -const ConfigKey kConfigKey(0, 12345); -const int tagId = 1; -const int64_t metricId = 123; -const uint64_t protoHash = 0x123456789; -const int logEventMatcherIndex = 0; -const int64_t bucketStartTimeNs = 10 * NS_PER_SEC; -const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; -const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; -const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; -const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - -shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1, - int32_t value2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, value1); - AStatsEvent_writeString(statsEvent, str1.c_str()); - AStatsEvent_writeInt32(statsEvent, value2); - - shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} -} // anonymous namespace - -// Setup for parameterized tests. -class GaugeMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {}; - -INSTANTIATE_TEST_SUITE_P(GaugeMetricProducerTest_PartialBucket, - GaugeMetricProducerTest_PartialBucket, - testing::Values(APP_UPGRADE, BOOT_COMPLETE)); - -/* - * Tests that the first bucket works correctly - */ -TEST(GaugeMetricProducerTest, TestFirstBucket) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.mutable_gauge_fields_filter()->set_include_all(false); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(1); - gaugeFieldMatcher->add_child()->set_field(3); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - // statsd started long ago. - // The metric starts in the middle of the bucket - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum); - EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs()); -} - -TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.mutable_gauge_fields_filter()->set_include_all(false); - metric.set_max_pull_delay_sec(INT_MAX); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(1); - gaugeFieldMatcher->add_child()->set_field(3); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11)); - return true; - })); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11)); - - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(10, it->mValue.int_value); - it++; - EXPECT_EQ(11, it->mValue.int_value); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin() - ->second.back() - .mGaugeAtoms.front() - .mFields->begin() - ->mValue.int_value); - - allData.clear(); - allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(24, it->mValue.int_value); - it++; - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(25, it->mValue.int_value); - // One dimension. - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); - it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(10L, it->mValue.int_value); - it++; - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(11L, it->mValue.int_value); - - gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs); - ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); - // One dimension. - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - ASSERT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size()); - it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(24L, it->mValue.int_value); - it++; - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(25L, it->mValue.int_value); -} - -TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) { - sp<AlarmMonitor> alarmMonitor; - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.mutable_gauge_fields_filter()->set_include_all(true); - - Alert alert; - alert.set_id(101); - alert.set_metric_id(metricId); - alert.set_trigger_if_sum_gt(25); - alert.set_num_buckets(100); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - - sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); - EXPECT_TRUE(anomalyTracker != nullptr); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); - - switch (GetParam()) { - case APP_UPGRADE: - gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(partialBucketSplitTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); - EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); - // Partial buckets are not sent to anomaly tracker. - EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - - // Create an event in the same partial bucket. - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(partialBucketSplitTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ((int64_t)partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); - // Partial buckets are not sent to anomaly tracker. - EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - - // Next event should trigger creation of new bucket and send previous full bucket to anomaly - // tracker. - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum); - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - - // Next event should trigger creation of new bucket. - LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 125 * NS_PER_SEC, 1, 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum); - ASSERT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -} - -TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_max_pull_delay_sec(INT_MAX); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(2); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - .WillOnce(Return(false)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2)); - return true; - })); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - - switch (GetParam()) { - case APP_UPGRADE: - gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(partialBucketSplitTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); - EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); -} - -TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_max_pull_delay_sec(INT_MAX); - metric.set_split_bucket_for_app_upgrade(false); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(2); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Return(false)); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - - gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - ASSERT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); - EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); -} - -TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_max_pull_delay_sec(INT_MAX); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(2); - metric.set_condition(StringToId("SCREEN_ON")); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - int64_t conditionChangeNs = bucketStartTimeNs + 8; - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100)); - return true; - })); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - - gaugeProducer.onConditionChanged(true, conditionChangeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size()); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin() - ->second.back() - .mGaugeAtoms.front() - .mFields->begin() - ->mValue.int_value); - - gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10); - gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin() - ->second.back() - .mGaugeAtoms.front() - .mFields->begin() - ->mValue.int_value); -} - -TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { - const int conditionTag = 65; - GaugeMetric metric; - metric.set_id(1111111); - metric.set_bucket(ONE_MINUTE); - metric.mutable_gauge_fields_filter()->set_include_all(true); - metric.set_condition(StringToId("APP_DIED")); - metric.set_max_pull_delay_sec(INT_MAX); - auto dim = metric.mutable_dimensions_in_what(); - dim->set_field(tagId); - dim->add_child()->set_field(1); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EXPECT_CALL(*wizard, query(_, _, _)) - .WillRepeatedly( - Invoke([](const int conditionIndex, const ConditionKey& conditionParameters, - const bool isPartialLink) { - int pos[] = {1, 0, 0}; - Field f(conditionTag, pos, 0); - HashableDimensionKey key; - key.mutableValues()->emplace_back(f, Value((int32_t)1000000)); - - return ConditionState::kTrue; - })); - - int64_t sliceConditionChangeNs = bucketStartTimeNs + 8; - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100)); - return true; - })); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - - gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs); - - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first; - ASSERT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - - ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size()); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -} - -TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { - sp<AlarmMonitor> alarmMonitor; - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Return(false)); - - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_max_pull_delay_sec(INT_MAX); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(2); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - Alert alert; - alert.set_id(101); - alert.set_metric_id(metricId); - alert.set_trigger_if_sum_gt(25); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 60; - alert.set_refractory_period_secs(refPeriodSec); - sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); - - int tagId = 1; - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); - - std::shared_ptr<LogEvent> event2 = - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 20, 15); - - allData.clear(); - allData.push_back(event2); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec); - - allData.clear(); - allData.push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); - - // This event does not have the gauge field. Thus the current bucket value is 0. - allData.clear(); - allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty()); -} - -TEST(GaugeMetricProducerTest, TestPullOnTrigger) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); - metric.mutable_gauge_fields_filter()->set_include_all(false); - metric.set_max_pull_delay_sec(INT_MAX); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(1); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5)); - return true; - })) - .WillOnce(Return(true)); - - int triggerId = 5; - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); - - LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); - triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); - triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size()); - EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin() - ->second.back() - .mGaugeAtoms[0] - .mFields->begin() - ->mValue.int_value); - EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin() - ->second.back() - .mGaugeAtoms[1] - .mFields->begin() - ->mValue.int_value); -} - -TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); - metric.mutable_gauge_fields_filter()->set_include_all(true); - metric.set_max_pull_delay_sec(INT_MAX); - auto dimensionMatcher = metric.mutable_dimensions_in_what(); - // use field 1 as dimension. - dimensionMatcher->set_field(tagId); - dimensionMatcher->add_child()->set_field(1); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6)); - return true; - })) - .WillOnce(Return(true)); - - int triggerId = 5; - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size()); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); - triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); - triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.size()); - auto bucketIt = gaugeProducer.mPastBuckets.begin(); - ASSERT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size()); - EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); - EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); - bucketIt++; - ASSERT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size()); - EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); - EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); - EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value); -} - -/* - * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size - * is smaller than the "min_bucket_size_nanos" specified in the metric config. - */ -TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(FIVE_MINUTES); - metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); - metric.set_min_bucket_size_nanos(10000000000); // 10 seconds - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _)) - // Bucket start. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10)); - return true; - })); - - int triggerId = 5; - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, true, - FAST /* dump_latency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_gauge_metrics()); - ASSERT_EQ(0, report.gauge_metrics().data_size()); - ASSERT_EQ(1, report.gauge_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.gauge_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), - report.gauge_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp deleted file mode 100644 index fda3daaa56aa..000000000000 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ /dev/null @@ -1,424 +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. - -#include "src/metrics/duration_helper/MaxDurationTracker.h" -#include "src/condition/ConditionWizard.h" -#include "metrics_test_helper.h" -#include "tests/statsd_test_util.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <stdio.h> -#include <set> -#include <unordered_map> -#include <vector> - -using namespace android::os::statsd; -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -const ConfigKey kConfigKey(0, 12345); - -const int TagId = 1; - -const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1"); -const HashableDimensionKey conditionKey = getMockedDimensionKey(TagId, 4, "1"); -const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); -const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); -const int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - -TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); - const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - - int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey()); - // Event starts again. This would not change anything as it already starts. - tracker.noteStart(key1, true, bucketStartTimeNs + 3, ConditionKey()); - // Stopped. - tracker.noteStop(key1, bucketStartTimeNs + 10, false); - - // Another event starts in this bucket. - tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey()); - tracker.noteStop(key2, bucketStartTimeNs + 40, false /*stop all*/); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(20LL, buckets[eventKey][0].mDuration); -} - -TEST(MaxDurationTrackerTest, TestStopAll) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); - const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - - int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey()); - - // Another event starts in this bucket. - tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey()); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets); - tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40); - EXPECT_TRUE(tracker.mInfos.empty()); - EXPECT_TRUE(buckets.find(eventKey) == buckets.end()); - - tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(bucketSizeNs + 40 - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[eventKey][0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs); -} - -TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); - const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - - int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - // The event starts. - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); - - // Starts again. Does not DEFAULT_DIMENSION_KEY anything. - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1, - ConditionKey()); - - // The event stops at early 4th bucket. - // Notestop is called from DurationMetricProducer's onMatchedLogEvent, which calls - // flushIfneeded. - tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets); - tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (3 * bucketSizeNs) + 20, - false /*stop all*/); - EXPECT_TRUE(buckets.find(eventKey) == buckets.end()); - - tracker.flushIfNeeded(bucketStartTimeNs + 4 * bucketSizeNs, &buckets); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ((3 * bucketSizeNs) + 20 - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[eventKey][0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs); -} - -TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); - const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - - int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - // 2 starts - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 10, ConditionKey()); - // one stop - tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 20, false /*stop all*/); - - tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1, &buckets); - // Because of nesting, still not stopped. - EXPECT_TRUE(buckets.find(eventKey) == buckets.end()); - - // real stop now. - tracker.noteStop(DEFAULT_DIMENSION_KEY, - bucketStartTimeNs + (2 * bucketSizeNs) + 5, false); - tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets); - - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(2 * bucketSizeNs + 5 - 1, buckets[eventKey][0].mDuration); -} - -TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { - const HashableDimensionKey conditionDimKey = key1; - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - ConditionKey conditionKey1; - MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 1, "1"); - conditionKey1[StringToId("APP_BACKGROUND")] = conditionDimKey; - - /** - Start in first bucket, stop in second bucket. Condition turns on and off in the first bucket - and again turns on and off in the second bucket. - */ - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t eventStartTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; - int64_t conditionStarts1 = bucketStartTimeNs + 11 * NS_PER_SEC; - int64_t conditionStops1 = bucketStartTimeNs + 14 * NS_PER_SEC; - int64_t conditionStarts2 = bucketStartTimeNs + bucketSizeNs + 5 * NS_PER_SEC; - int64_t conditionStops2 = conditionStarts2 + 10 * NS_PER_SEC; - int64_t eventStopTimeNs = conditionStops2 + 8 * NS_PER_SEC; - - int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, - 0, bucketStartTimeNs, bucketSizeNs, true, false, {}); - EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); - - tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1); - tracker.noteConditionChanged(key1, true, conditionStarts1); - tracker.noteConditionChanged(key1, false, conditionStops1); - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - ASSERT_EQ(0U, buckets.size()); - - tracker.noteConditionChanged(key1, true, conditionStarts2); - tracker.noteConditionChanged(key1, false, conditionStops2); - tracker.noteStop(key1, eventStopTimeNs, false); - tracker.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1, &buckets); - ASSERT_EQ(1U, buckets.size()); - vector<DurationBucket> item = buckets.begin()->second; - ASSERT_EQ(1UL, item.size()); - EXPECT_EQ((int64_t)(13LL * NS_PER_SEC), item[0].mDuration); -} - -TEST(MaxDurationTrackerTest, TestAnomalyDetection) { - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - ConditionKey conditionKey1; - MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); - conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = 13000000000; - int64_t durationTimeNs = 2 * 1000; - - int64_t metricId = 1; - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 45; - alert.set_refractory_period_secs(refPeriodSec); - sp<AlarmMonitor> alarmMonitor; - sp<DurationAnomalyTracker> anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, - {anomalyTracker}); - - tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1); - sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(53ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); - - // Remove the anomaly alarm when the duration is no longer fully met. - tracker.noteConditionChanged(key1, false, eventStartTimeNs + 15 * NS_PER_SEC); - ASSERT_EQ(0U, anomalyTracker->mAlarms.size()); - - // Since the condition was off for 10 seconds, the anomaly should trigger 10 sec later. - tracker.noteConditionChanged(key1, true, eventStartTimeNs + 25 * NS_PER_SEC); - ASSERT_EQ(1U, anomalyTracker->mAlarms.size()); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(63ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); -} - -// This tests that we correctly compute the predicted time of an anomaly assuming that the current -// state continues forward as-is. -TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) { - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - ConditionKey conditionKey1; - MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); - conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; - ConditionKey conditionKey2; - conditionKey2[StringToId("APP_BACKGROUND")] = getMockedDimensionKey(TagId, 4, "2"); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - /** - * Suppose we have two sub-dimensions that we're taking the MAX over. In the first of these - * nested dimensions, we enter the pause state after 3 seconds. When we resume, the second - * dimension has already been running for 4 seconds. Thus, we have 40-4=36 seconds remaining - * before we trigger the anomaly. - */ - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC; // Condition is off at start. - int64_t conditionStarts1 = bucketStartTimeNs + 11 * NS_PER_SEC; - int64_t conditionStops1 = bucketStartTimeNs + 14 * NS_PER_SEC; - int64_t conditionStarts2 = bucketStartTimeNs + 20 * NS_PER_SEC; - int64_t eventStartTimeNs2 = conditionStarts2 - 4 * NS_PER_SEC; - - int64_t metricId = 1; - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 45; - alert.set_refractory_period_secs(refPeriodSec); - sp<AlarmMonitor> alarmMonitor; - sp<DurationAnomalyTracker> anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, - {anomalyTracker}); - - tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1); - tracker.noteConditionChanged(key1, true, conditionStarts1); - tracker.noteConditionChanged(key1, false, conditionStops1); - tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2); // Condition is on already. - tracker.noteConditionChanged(key1, true, conditionStarts2); - ASSERT_EQ(1U, anomalyTracker->mAlarms.size()); - auto alarm = anomalyTracker->mAlarms.begin()->second; - int64_t anomalyFireTimeSec = alarm->timestampSec; - EXPECT_EQ(conditionStarts2 + 36 * NS_PER_SEC, - (long long)anomalyFireTimeSec * NS_PER_SEC); - - // Now we test the calculation now that there's a refractory period. - // At the correct time, declare the anomaly. This will set a refractory period. Make sure it - // gets correctly taken into account in future predictAnomalyTimestampNs calculations. - std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarms({alarm}); - anomalyTracker->informAlarmsFired(anomalyFireTimeSec * NS_PER_SEC, firedAlarms); - ASSERT_EQ(0u, anomalyTracker->mAlarms.size()); - int64_t refractoryPeriodEndsSec = anomalyFireTimeSec + refPeriodSec; - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), refractoryPeriodEndsSec); - - // Now stop and start again. Make sure the new predictAnomalyTimestampNs takes into account - // the refractory period correctly. - int64_t eventStopTimeNs = anomalyFireTimeSec * NS_PER_SEC + 10; - tracker.noteStop(key1, eventStopTimeNs, false); - tracker.noteStop(key2, eventStopTimeNs, false); - tracker.noteStart(key1, true, eventStopTimeNs + 1000000, conditionKey1); - // Anomaly is ongoing, but we're still in the refractory period. - ASSERT_EQ(1U, anomalyTracker->mAlarms.size()); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ(refractoryPeriodEndsSec, (long long)(alarm->timestampSec)); - - // Makes sure it is correct after the refractory period is over. - tracker.noteStop(key1, eventStopTimeNs + 2000000, false); - int64_t justBeforeRefPeriodNs = (refractoryPeriodEndsSec - 2) * NS_PER_SEC; - tracker.noteStart(key1, true, justBeforeRefPeriodNs, conditionKey1); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ(justBeforeRefPeriodNs + 40 * NS_PER_SEC, - (unsigned long long)(alarm->timestampSec * NS_PER_SEC)); -} - -// Suppose that within one tracker there are two dimensions A and B. -// Suppose A starts, then B starts, and then A stops. We still need to set an anomaly based on the -// elapsed duration of B. -TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) { - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - ConditionKey conditionKey1; - MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); - conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; - ConditionKey conditionKey2; - conditionKey2[StringToId("APP_BACKGROUND")] = getMockedDimensionKey(TagId, 4, "2"); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - /** - * Suppose we have two sub-dimensions that we're taking the MAX over. In the first of these - * nested dimensions, are started for 8 seconds. When we stop, the other nested dimension has - * been started for 5 seconds. So we can only allow 35 more seconds from now. - */ - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - int64_t eventStartTimeNs1 = bucketStartTimeNs + 5 * NS_PER_SEC; // Condition is off at start. - int64_t eventStopTimeNs1 = bucketStartTimeNs + 13 * NS_PER_SEC; - int64_t eventStartTimeNs2 = bucketStartTimeNs + 8 * NS_PER_SEC; - - int64_t metricId = 1; - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 45; - alert.set_refractory_period_secs(refPeriodSec); - sp<AlarmMonitor> alarmMonitor; - sp<DurationAnomalyTracker> anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, - {anomalyTracker}); - - tracker.noteStart(key1, true, eventStartTimeNs1, conditionKey1); - tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2); - tracker.noteStop(key1, eventStopTimeNs1, false); - ASSERT_EQ(1U, anomalyTracker->mAlarms.size()); - auto alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ(eventStopTimeNs1 + 35 * NS_PER_SEC, - (unsigned long long)(alarm->timestampSec * NS_PER_SEC)); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp deleted file mode 100644 index 1d6f7de5e7e3..000000000000 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ /dev/null @@ -1,576 +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. - -#include "src/metrics/duration_helper/OringDurationTracker.h" -#include "src/condition/ConditionWizard.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 <set> -#include <unordered_map> -#include <vector> - -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ -namespace android { -namespace os { -namespace statsd { - -const ConfigKey kConfigKey(0, 12345); -const int TagId = 1; -const int64_t metricId = 123; -const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event"); - -const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(TagId, 1, "maps"); -const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); -const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); -const int64_t bucketSizeNs = 30 * NS_PER_SEC; - -TEST(OringDurationTrackerTest, TestDurationOverlap) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t durationTimeNs = 2 * 1000; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - false, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); - EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl - EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); - - tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false); - tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(durationTimeNs, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestDurationNested) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl - - tracker.noteStop(kEventKey1, eventStartTimeNs + 2000, false); - tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(2003LL, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestStopAll) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const std::vector<HashableDimensionKey> kConditionKey1 = - {getMockedDimensionKey(TagId, 1, "maps")}; - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); - tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl - - tracker.noteStopAll(eventStartTimeNs + 2003); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(2003LL, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t durationTimeNs = 2 * 1000; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); - EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, ConditionKey()); - EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime); - - ASSERT_EQ(2u, buckets[eventKey].size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); - - tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 10, false); - tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 12, false); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(2u, buckets[eventKey].size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); -} - -TEST(OringDurationTrackerTest, TestDurationConditionChange) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - - EXPECT_CALL(*wizard, query(_, key1, _)) // #4 - .WillOnce(Return(ConditionState::kFalse)); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t durationTimeNs = 2 * 1000; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - true, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); - - tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 5); - - tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(5LL, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestDurationConditionChange2) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - - EXPECT_CALL(*wizard, query(_, key1, _)) - .Times(2) - .WillOnce(Return(ConditionState::kFalse)) - .WillOnce(Return(ConditionState::kTrue)); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t durationTimeNs = 2 * 1000; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - true, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); - // condition to false; record duration 5n - tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 5); - // condition to true. - tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 1000); - // 2nd duration: 1000ns - tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(1005LL, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - - EXPECT_CALL(*wizard, query(_, key1, _)) // #4 - .WillOnce(Return(ConditionState::kFalse)); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1); - - tracker.noteStop(kEventKey1, eventStartTimeNs + 3, false); - - tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 15); - - tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(15LL, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - alert.set_refractory_period_secs(1); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - int64_t bucketStartTimeNs = 10 * NS_PER_SEC; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - - sp<AlarmMonitor> alarmMonitor; - sp<DurationAnomalyTracker> anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, - {anomalyTracker}); - - // Nothing in the past bucket. - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); - EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs), - tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs)); - - tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 3, false); - ASSERT_EQ(0u, buckets[eventKey].size()); - - int64_t event1StartTimeNs = eventStartTimeNs + 10; - tracker.noteStart(kEventKey1, true, event1StartTimeNs, ConditionKey()); - // No past buckets. The anomaly will happen in bucket #0. - EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3), - tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs)); - - int64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10; - tracker.flushIfNeeded(event1StopTimeNs, &buckets); - tracker.noteStop(kEventKey1, event1StopTimeNs, false); - - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(3LL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10, - buckets[eventKey][0].mDuration); - - const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10; - const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs; - - // One past buckets. The anomaly will happen in bucket #1. - int64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15; - tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey()); - EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration - - bucket1Duration), - tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs)); - tracker.noteStop(kEventKey1, event2StartTimeNs + 1, false); - - // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in - // bucket #2. - int64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC; - tracker.noteStart(kEventKey1, true, event3StartTimeNs, ConditionKey()); - EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL), - tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs)); -} - -TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp2) { - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(5 * NS_PER_SEC); - alert.set_num_buckets(1); - alert.set_refractory_period_secs(20); - - int64_t bucketStartTimeNs = 10 * NS_PER_SEC; - int64_t bucketNum = 0; - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<AlarmMonitor> alarmMonitor; - sp<DurationAnomalyTracker> anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard, 1, - - true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, true, false, {anomalyTracker}); - - int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC; - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); - // Anomaly happens in the bucket #1. - EXPECT_EQ((long long)(bucketStartTimeNs + 14 * NS_PER_SEC), - tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs)); - - tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 14 * NS_PER_SEC, false); - - EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY)); - - int64_t event2StartTimeNs = bucketStartTimeNs + 22 * NS_PER_SEC; - EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY)); - EXPECT_EQ((long long)(bucketStartTimeNs + 35 * NS_PER_SEC), - tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs)); -} - -TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp3) { - // Test the cases where the refractory period is smaller than the bucket size, longer than - // the bucket size, and longer than 2x of the anomaly detection window. - for (int j = 0; j < 3; j++) { - int64_t thresholdNs = j * bucketSizeNs + 5 * NS_PER_SEC; - for (int i = 0; i <= 7; ++i) { - - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(thresholdNs); - alert.set_num_buckets(3); - alert.set_refractory_period_secs( - bucketSizeNs / NS_PER_SEC / 2 + i * bucketSizeNs / NS_PER_SEC); - - int64_t bucketStartTimeNs = 10 * NS_PER_SEC; - int64_t bucketNum = 101; - - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<AlarmMonitor> alarmMonitor; - sp<DurationAnomalyTracker> anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard, - 1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, true, false, {anomalyTracker}); - - int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC; - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); - EXPECT_EQ((long long)(eventStartTimeNs + thresholdNs), - tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs)); - int64_t eventStopTimeNs = eventStartTimeNs + thresholdNs + NS_PER_SEC; - tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStopTimeNs, false); - - int64_t refractoryPeriodEndSec = - anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY); - EXPECT_EQ(eventStopTimeNs / (int64_t)NS_PER_SEC + alert.refractory_period_secs(), - refractoryPeriodEndSec); - - // Acquire and release a wakelock in the next bucket. - int64_t event2StartTimeNs = eventStopTimeNs + bucketSizeNs; - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, event2StartTimeNs, ConditionKey()); - int64_t event2StopTimeNs = event2StartTimeNs + 4 * NS_PER_SEC; - tracker.noteStop(DEFAULT_DIMENSION_KEY, event2StopTimeNs, false); - - // Test the alarm prediction works well when seeing another wakelock start event. - for (int k = 0; k <= 2; ++k) { - int64_t event3StartTimeNs = event2StopTimeNs + NS_PER_SEC + k * bucketSizeNs; - int64_t alarmTimestampNs = - tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs); - EXPECT_GT(alarmTimestampNs, 0u); - EXPECT_GE(alarmTimestampNs, event3StartTimeNs); - EXPECT_GE(alarmTimestampNs, refractoryPeriodEndSec *(int64_t) NS_PER_SEC); - } - } - } -} - -TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 45; - alert.set_refractory_period_secs(refPeriodSec); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - - int64_t bucketStartTimeNs = 10 * NS_PER_SEC; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - - sp<AlarmMonitor> alarmMonitor; - sp<DurationAnomalyTracker> anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/, - bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - false, false, {anomalyTracker}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); - tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - EXPECT_TRUE(tracker.mStarted.empty()); - EXPECT_EQ(10LL, tracker.mStateKeyDurationMap[DEFAULT_DIMENSION_KEY].mDuration); // 10ns - - ASSERT_EQ(0u, tracker.mStarted.size()); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey()); - ASSERT_EQ(1u, anomalyTracker->mAlarms.size()); - EXPECT_EQ((long long)(52ULL * NS_PER_SEC), // (10s + 1s + 1ns + 20ns) - 10ns + 40s, rounded up - (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC)); - // The alarm is set to fire at 52s, and when it does, an anomaly would be declared. However, - // because this is a unit test, the alarm won't actually fire at all. Since the alarm fails - // to fire in time, the anomaly is instead caught when noteStop is called, at around 71s. - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets); - tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 25, false); - EXPECT_EQ(anomalyTracker->getSumOverPastBuckets(eventKey), (long long)(bucketSizeNs)); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), - std::ceil((eventStartTimeNs + 2 * bucketSizeNs + 25.0) / NS_PER_SEC + refPeriodSec)); -} - -TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 45; - alert.set_refractory_period_secs(refPeriodSec); - - unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - ConditionKey conkey; - conkey[StringToId("APP_BACKGROUND")] = kConditionKey1; - int64_t bucketStartTimeNs = 10 * NS_PER_SEC; - int64_t bucketSizeNs = 30 * NS_PER_SEC; - - sp<AlarmMonitor> alarmMonitor; - sp<DurationAnomalyTracker> anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/, - bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, false, - false, {anomalyTracker}); - - tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1 - ASSERT_EQ(1u, anomalyTracker->mAlarms.size()); - sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - - tracker.noteStop(kEventKey1, 17 * NS_PER_SEC, false); // stop key1 (2 seconds later) - ASSERT_EQ(0u, anomalyTracker->mAlarms.size()); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - - tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again - ASSERT_EQ(1u, anomalyTracker->mAlarms.size()); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - - tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2 - ASSERT_EQ(1u, anomalyTracker->mAlarms.size()); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - - tracker.noteStop(kEventKey1, 47 * NS_PER_SEC, false); // stop key1 - ASSERT_EQ(1u, anomalyTracker->mAlarms.size()); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - - // Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time. - std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarms({alarm}); - anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms); - ASSERT_EQ(0u, anomalyTracker->mAlarms.size()); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec); - - tracker.noteStop(kEventKey2, 69 * NS_PER_SEC, false); // stop key2 - ASSERT_EQ(0u, anomalyTracker->mAlarms.size()); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp deleted file mode 100644 index 6cf4192b41fd..000000000000 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ /dev/null @@ -1,6919 +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. - -#include "src/metrics/ValueMetricProducer.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <math.h> -#include <stdio.h> - -#include <vector> - -#include "metrics_test_helper.h" -#include "src/matchers/SimpleAtomMatchingTracker.h" -#include "src/metrics/MetricProducer.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using std::make_shared; -using std::set; -using std::shared_ptr; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -namespace { - -const ConfigKey kConfigKey(0, 12345); -const int tagId = 1; -const int64_t metricId = 123; -const uint64_t protoHash = 0x1234567890; -const int logEventMatcherIndex = 0; -const int64_t bucketStartTimeNs = 10000000000; -const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; -const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; -const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; -const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs; -const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs; -double epsilon = 0.001; - -static void assertPastBucketValuesSingleKey( - const std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>>& mPastBuckets, - const std::initializer_list<int>& expectedValuesList, - const std::initializer_list<int64_t>& expectedDurationNsList, - const std::initializer_list<int64_t>& expectedStartTimeNsList, - const std::initializer_list<int64_t>& expectedEndTimeNsList) { - vector<int> expectedValues(expectedValuesList); - vector<int64_t> expectedDurationNs(expectedDurationNsList); - vector<int64_t> expectedStartTimeNs(expectedStartTimeNsList); - vector<int64_t> expectedEndTimeNs(expectedEndTimeNsList); - - ASSERT_EQ(expectedValues.size(), expectedDurationNs.size()); - ASSERT_EQ(expectedValues.size(), expectedStartTimeNs.size()); - ASSERT_EQ(expectedValues.size(), expectedEndTimeNs.size()); - - if (expectedValues.size() == 0) { - ASSERT_EQ(0, mPastBuckets.size()); - return; - } - - ASSERT_EQ(1, mPastBuckets.size()); - ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size()); - - const vector<PastValueBucket>& buckets = mPastBuckets.begin()->second; - for (int i = 0; i < expectedValues.size(); i++) { - EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value) - << "Values differ at index " << i; - EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs) - << "Condition duration value differ at index " << i; - EXPECT_EQ(expectedStartTimeNs[i], buckets[i].mBucketStartNs) - << "Start time differs at index " << i; - EXPECT_EQ(expectedEndTimeNs[i], buckets[i].mBucketEndNs) - << "End time differs at index " << i; - } -} - -static void assertConditionTimer(const ConditionTimer& conditionTimer, bool condition, - int64_t timerNs, int64_t lastConditionTrueTimestampNs) { - EXPECT_EQ(condition, conditionTimer.mCondition); - EXPECT_EQ(timerNs, conditionTimer.mTimerNs); - EXPECT_EQ(lastConditionTrueTimestampNs, conditionTimer.mLastConditionChangeTimestampNs); -} - -} // anonymous namespace - -class ValueMetricProducerTestHelper { -public: - static sp<ValueMetricProducer> createValueProducerNoConditions( - sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) { - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)) - .WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)) - .WillRepeatedly(Return()); - - sp<ValueMetricProducer> valueProducer = - new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); - return valueProducer; - } - - static sp<ValueMetricProducer> createValueProducerWithCondition( - sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric, - ConditionState conditionAfterFirstBucketPrepared) { - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)) - .WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)) - .WillRepeatedly(Return()); - - sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard, - protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); - valueProducer->mCondition = conditionAfterFirstBucketPrepared; - return valueProducer; - } - - static sp<ValueMetricProducer> createValueProducerWithState( - sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric, - vector<int32_t> slicedStateAtoms, - unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) { - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)) - .WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)) - .WillRepeatedly(Return()); - - sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, -1 /* no condition */, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap); - valueProducer->prepareFirstBucket(); - return valueProducer; - } - - static sp<ValueMetricProducer> createValueProducerWithConditionAndState( - sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric, - vector<int32_t> slicedStateAtoms, - unordered_map<int, unordered_map<int, int64_t>> stateGroupMap, - ConditionState conditionAfterFirstBucketPrepared) { - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)) - .WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)) - .WillRepeatedly(Return()); - - sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, 0 /* condition tracker index */, {ConditionState::kUnknown}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, - stateGroupMap); - valueProducer->prepareFirstBucket(); - valueProducer->mCondition = conditionAfterFirstBucketPrepared; - return valueProducer; - } - - static ValueMetric createMetric() { - ValueMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.mutable_value_field()->set_field(tagId); - metric.mutable_value_field()->add_child()->set_field(2); - metric.set_max_pull_delay_sec(INT_MAX); - return metric; - } - - static ValueMetric createMetricWithCondition() { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_condition(StringToId("SCREEN_ON")); - return metric; - } - - static ValueMetric createMetricWithState(string state) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.add_slice_by_state(StringToId(state)); - return metric; - } - - static ValueMetric createMetricWithConditionAndState(string state) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_condition(StringToId("SCREEN_ON")); - metric.add_slice_by_state(StringToId(state)); - return metric; - } -}; - -// Setup for parameterized tests. -class ValueMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {}; - -INSTANTIATE_TEST_SUITE_P(ValueMetricProducerTest_PartialBucket, - ValueMetricProducerTest_PartialBucket, - testing::Values(APP_UPGRADE, BOOT_COMPLETE)); - -/* - * Tests that the first bucket works correctly - */ -TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - int64_t startTimeBase = 11; - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - // statsd started long ago. - // The metric starts in the middle of the bucket - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - -1, startTimeBase, 22, pullerManager); - valueProducer.prepareFirstBucket(); - - EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10)); - EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10)); - EXPECT_EQ(60 * NS_PER_SEC + startTimeBase, - valueProducer.calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC)); - EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase, - valueProducer.calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC)); -} - -/* - * Tests that the first bucket works correctly - */ -TEST(ValueMetricProducerTest, TestFirstBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - // statsd started long ago. - // The metric starts in the middle of the bucket - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - -1, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); - valueProducer.prepareFirstBucket(); - - EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(10, valueProducer.mCurrentBucketNum); - EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs()); -} - -/* - * Tests pulled atoms with no conditions - */ -TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(8, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(23, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(12, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(36, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(13, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs); - EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2; - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Initialize bucket. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); - return true; - })) - // Partial bucket. - .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&, - const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs + 8, 5)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - // First bucket ends. - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 2)); - valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs); - - // Partial buckets created in 2nd bucket. - switch (GetParam()) { - case APP_UPGRADE: - valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs); - EXPECT_EQ(1, valueProducer->getCurrentBucketNum()); - - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1, 3}, - {bucketSizeNs, partialBucketSplitTimeNs - bucket2StartTimeNs}, - {bucketStartTimeNs, bucket2StartTimeNs}, - {bucket2StartTimeNs, partialBucketSplitTimeNs}); -} - -/* - * Tests pulled atoms with filtering - */ -TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - FieldValueMatcher fvm; - fvm.set_field(1); - fvm.set_eq_int(3); - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex, {fvm}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard, - protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 3, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(8, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket3StartTimeNs + 1, 4, 23)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // No new data seen, so data has been cleared. - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(8, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - - // the base was reset - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(36, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs); -} - -/* - * Tests pulled atoms with no conditions and take absolute value after reset - */ -TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_use_absolute_value_on_reset(true); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Return(true)); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(10, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(10, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(36, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(26, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size()); - EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs); -} - -/* - * Tests pulled atoms with no conditions and take zero value after reset - */ -TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Return(false)); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(10, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(36, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(26, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); -} - -/* - * Test pulled event with non sliced condition. - */ -TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 1); // Third condition change. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - // startUpdated:false sum:0 start:100 - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(110, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(10, curInterval.value.long_value); - - valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(20, curInterval.value.long_value); - EXPECT_EQ(false, curBaseInfo.hasBase); - - valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1}, - {bucketStartTimeNs, bucket2StartTimeNs}, - {bucket2StartTimeNs, bucket3StartTimeNs}); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, -1, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150; - switch (GetParam()) { - case APP_UPGRADE: - valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, - {partialBucketSplitTimeNs - bucketStartTimeNs}, - {bucketStartTimeNs}, {partialBucketSplitTimeNs}); - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(0, valueProducer.getCurrentBucketNum()); - - // Event arrives after the bucket split. - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 20); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, - {partialBucketSplitTimeNs - bucketStartTimeNs}, - {bucketStartTimeNs}, {partialBucketSplitTimeNs}); - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(0, valueProducer.getCurrentBucketNum()); - - // Next value should create a new bucket. - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 5 * NS_PER_SEC, 10); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10, 20}, - {partialBucketSplitTimeNs - bucketStartTimeNs, - bucket2StartTimeNs - partialBucketSplitTimeNs}, - {bucketStartTimeNs, partialBucketSplitTimeNs}, - {partialBucketSplitTimeNs, bucket2StartTimeNs}); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, valueProducer.getCurrentBucketNum()); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150; - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - .WillOnce(Return(true)) - .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&, - const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120)); - return true; - })); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100)); - - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - - switch (GetParam()) { - case APP_UPGRADE: - valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, valueProducer.getCurrentBucketNum()); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150}, {bucket2StartTimeNs}, - {partialBucketSplitTimeNs}); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150)); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(2, valueProducer.getCurrentBucketNum()); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30}, {150, bucketSizeNs - 150}, - {bucket2StartTimeNs, partialBucketSplitTimeNs}, - {partialBucketSplitTimeNs, bucket3StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_split_bucket_for_app_upgrade(false); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Return(true)); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100)); - - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - - valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150); - ASSERT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, - bucket2StartTimeNs - 100); // Condition change to false time. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 120)); - return true; - })); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); - - valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100); - EXPECT_FALSE(valueProducer->mCondition); - - int64_t partialBucketSplitTimeNs = bucket2StartTimeNs - 50; - switch (GetParam()) { - case APP_UPGRADE: - valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - // Expect one full buckets already done and starting a partial bucket. - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs); - EXPECT_EQ(0, valueProducer->getCurrentBucketNum()); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, - {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)}, - {bucketStartTimeNs}, {partialBucketSplitTimeNs}); - EXPECT_FALSE(valueProducer->mCondition); -} - -TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, -1, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(30, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, - protoHash, logEventMatcherIndex, eventMatcherWizard, -1, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - valueProducer.mCondition = ConditionState::kFalse; - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has 1 slice - ASSERT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); - - valueProducer.onConditionChangedLocked(true, bucketStartTimeNs + 15); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(20, curInterval.value.long_value); - - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 30); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(50, curInterval.value.long_value); - - valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 40); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(50, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20}, {bucketStartTimeNs}, - {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestAnomalyDetection) { - sp<AlarmMonitor> alarmMonitor; - Alert alert; - alert.set_id(101); - alert.set_metric_id(metricId); - alert.set_trigger_if_sum_gt(130); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 3; - alert.set_refractory_period_secs(refPeriodSec); - - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, - -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - valueProducer.prepareFirstBucket(); - - sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 1 * NS_PER_SEC, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 2 + NS_PER_SEC, 20); - - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event3, tagId, - bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, 130); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event4, tagId, - bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC, 1); - - LogEvent event5(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event5, tagId, - bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, 150); - - LogEvent event6(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event6, tagId, - bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC, 160); - - // Two events in bucket #0. - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - // Value sum == 30 <= 130. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); - - // One event in bucket #2. No alarm as bucket #0 is trashed out. - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - // Value sum == 130 <= 130. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); - - // Three events in bucket #3. - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - // Anomaly at event 4 since Value sum == 131 > 130! - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event5); - // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event6); - // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event6.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); -} - -TEST(ValueMetricProducerTest, TestAnomalyDetectionMultipleBucketsSkipped) { - sp<AlarmMonitor> alarmMonitor; - Alert alert; - alert.set_id(101); - alert.set_metric_id(metricId); - alert.set_trigger_if_sum_gt(100); - alert.set_num_buckets(1); - const int32_t refPeriodSec = 3; - alert.set_refractory_period_secs(refPeriodSec); - - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 0)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, - bucket3StartTimeNs + 100); // Condition changed to false time. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 100, 120)); - return true; - })); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - sp<AnomalyTracker> anomalyTracker = valueProducer->addAnomalyTracker(alert, alarmMonitor); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); - - // multiple buckets should be skipped here. - valueProducer->onConditionChanged(false, bucket3StartTimeNs + 100); - - // No alert is fired when multiple buckets are skipped. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); -} - -// Test value metric no condition, the pull on bucket boundary come in time and too late -TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Return(true)); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector<shared_ptr<LogEvent>> allData; - // pull 1 - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - - // startUpdated:true sum:0 start:11 - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - // pull 2 at correct time - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - // tartUpdated:false sum:12 - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(23, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}, - {bucket2StartTimeNs}, {bucket3StartTimeNs}); - - // pull 3 come late. - // The previous bucket gets closed with error. (Has start value 23, no ending) - // Another bucket gets closed with error. (No start, but ending with 36) - // The new bucket is back to normal. - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - // startUpdated:false sum:12 - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(36, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}, - {bucket2StartTimeNs}, {bucket3StartTimeNs}); - // The 1st bucket is dropped because of no data - // The 3rd bucket is dropped due to multiple buckets being skipped. - ASSERT_EQ(2, valueProducer->mSkippedBuckets.size()); - - EXPECT_EQ(bucketStartTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs); - EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs); - ASSERT_EQ(1, valueProducer->mSkippedBuckets[0].dropEvents.size()); - EXPECT_EQ(NO_DATA, valueProducer->mSkippedBuckets[0].dropEvents[0].reason); - EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].dropEvents[0].dropTimeNs); - - EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[1].bucketStartTimeNs); - EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].bucketEndTimeNs); - ASSERT_EQ(1, valueProducer->mSkippedBuckets[1].dropEvents.size()); - EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[1].dropEvents[0].reason); - EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].dropEvents[0].dropTimeNs); -} - -/* - * Test pulled event with non sliced condition. The pull on boundary come late because the alarm - * was delivered late. - */ -TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // condition becomes true - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })) - // condition becomes false - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120)); - return true; - })); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - // pull on bucket boundary come late, condition change happens before it - valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - EXPECT_EQ(false, curBaseInfo.hasBase); - - // Now the alarm is delivered. - // since the condition turned to off before this pull finish, it has no effect - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); -} - -/* - * Test pulled event with non sliced condition. The pull on boundary come late, after the condition - * change to false, and then true again. This is due to alarm delivered late. - */ -TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // condition becomes true - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })) - // condition becomes false - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120)); - return true; - })) - // condition becomes true again - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 25); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - // startUpdated:false sum:0 start:100 - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - // pull on bucket boundary come late, condition change happens before it - valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - - // condition changed to true again, before the pull alarm is delivered - valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(130, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - - // Now the alarm is delivered, but it is considered late, the data will be used - // for the new bucket since it was just pulled. - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50); - - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(140, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(10, curInterval.value.long_value); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - assertPastBucketValuesSingleKey( - valueProducer->mPastBuckets, {20, 30}, {bucketSizeNs - 8, bucketSizeNs - 24}, - {bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPushedAggregateMin) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_aggregation_type(ValueMetric::MIN); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, -1, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(10, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPushedAggregateMax) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_aggregation_type(ValueMetric::MAX); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, -1, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(20, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_aggregation_type(ValueMetric::AVG); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, -1, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval; - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(1, curInterval.sampleSize); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(25, curInterval.value.long_value); - EXPECT_EQ(2, curInterval.sampleSize); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer.mPastBuckets.size()); - ASSERT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - - EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value - - 12.5) < epsilon); -} - -TEST(ValueMetricProducerTest, TestPushedAggregateSum) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_aggregation_type(ValueMetric::SUM); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, -1, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(25, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_aggregation_type(ValueMetric::MIN); - metric.set_use_diff(true); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, -1, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(10, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(5, curInterval.value.long_value); - - // no change in data. - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(15, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(0, curInterval.value.long_value); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(15, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(0, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_value_field()->add_child()->set_field(3); - metric.set_aggregation_type(ValueMetric::MIN); - metric.set_use_diff(true); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, -1, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateThreeValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10, 20); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateThreeValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 1, 15, 22); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(10, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(20, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(5, curInterval.value.long_value); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1]; - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(2, curInterval.value.long_value); - - // no change in first value field - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateThreeValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 1, 15, 25); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(15, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(25, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateThreeValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15, 29); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(15, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(29, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - - ASSERT_EQ(1UL, valueProducer.mPastBuckets.size()); - ASSERT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); - ASSERT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size()); - ASSERT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size()); - - EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[0].mConditionTrueNs); - EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].valueIndex[0]); - EXPECT_EQ(2, valueProducer.mPastBuckets.begin()->second[0].values[1].long_value); - EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].valueIndex[1]); - - EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[1].mConditionTrueNs); - EXPECT_EQ(3, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value); - EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[1].valueIndex[0]); -} - -/* - * Tests zero default base. - */ -TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - metric.set_use_zero_default_base(true); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto iter = valueProducer->mCurrentSlicedBucket.begin(); - auto& interval1 = iter->second.intervals[0]; - auto iterBase = valueProducer->mCurrentBaseInfo.begin(); - auto& baseInfo1 = iterBase->second.baseInfos[0]; - EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(3, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - vector<shared_ptr<LogEvent>> allData; - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(11, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(8, interval1.value.long_value); - - auto it = valueProducer->mCurrentSlicedBucket.begin(); - for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { - if (it != iter) { - break; - } - } - auto itBase = valueProducer->mCurrentBaseInfo.begin(); - for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) { - if (itBase != iterBase) { - break; - } - } - EXPECT_TRUE(it != iter); - EXPECT_TRUE(itBase != iterBase); - auto& interval2 = it->second.intervals[0]; - auto& baseInfo2 = itBase->second.baseInfos[0]; - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(4, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_EQ(4, interval2.value.long_value); - - ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); - auto iterator = valueProducer->mPastBuckets.begin(); - EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); - EXPECT_EQ(8, iterator->second[0].values[0].long_value); - iterator++; - EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); - EXPECT_EQ(4, iterator->second[0].values[0].long_value); -} - -/* - * Tests using zero default base with failed pull. - */ -TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - metric.set_use_zero_default_base(true); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - const auto& it = valueProducer->mCurrentSlicedBucket.begin(); - ValueMetricProducer::Interval& interval1 = it->second.intervals[0]; - ValueMetricProducer::BaseInfo& baseInfo1 = - valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()) - ->second.baseInfos[0]; - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(3, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - vector<shared_ptr<LogEvent>> allData; - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(11, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(8, interval1.value.long_value); - - auto it2 = valueProducer->mCurrentSlicedBucket.begin(); - for (; it2 != valueProducer->mCurrentSlicedBucket.end(); it2++) { - if (it2 != it) { - break; - } - } - EXPECT_TRUE(it2 != it); - ValueMetricProducer::Interval& interval2 = it2->second.intervals[0]; - ValueMetricProducer::BaseInfo& baseInfo2 = - valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat()) - ->second.baseInfos[0]; - EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(4, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_EQ(4, interval2.value.long_value); - ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); - - // next pull somehow did not happen, skip to end of bucket 3 - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(5, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 13)); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 1, 5)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); - - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Get new references now that entries have been deleted from the map - const auto& it3 = valueProducer->mCurrentSlicedBucket.begin(); - const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin()); - ASSERT_EQ(it3->second.intervals.size(), 1); - ASSERT_EQ(it4->second.intervals.size(), 1); - ValueMetricProducer::Interval& interval3 = it3->second.intervals[0]; - ValueMetricProducer::Interval& interval4 = it4->second.intervals[0]; - ValueMetricProducer::BaseInfo& baseInfo3 = - valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat()) - ->second.baseInfos[0]; - ValueMetricProducer::BaseInfo& baseInfo4 = - valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat()) - ->second.baseInfos[0]; - - EXPECT_EQ(true, baseInfo3.hasBase); - EXPECT_EQ(5, baseInfo3.base.long_value); - EXPECT_EQ(false, interval3.hasValue); - EXPECT_EQ(5, interval3.value.long_value); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - EXPECT_EQ(true, baseInfo4.hasBase); - EXPECT_EQ(13, baseInfo4.base.long_value); - EXPECT_EQ(false, interval4.hasValue); - EXPECT_EQ(8, interval4.value.long_value); - - ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); -} - -/* - * Tests trim unused dimension key if no new data is seen in an entire bucket. - */ -TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto iter = valueProducer->mCurrentSlicedBucket.begin(); - auto& interval1 = iter->second.intervals[0]; - auto iterBase = valueProducer->mCurrentBaseInfo.begin(); - auto& baseInfo1 = iterBase->second.baseInfos[0]; - EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(3, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(11, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(8, interval1.value.long_value); - EXPECT_FALSE(interval1.seenNewData); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - - auto it = valueProducer->mCurrentSlicedBucket.begin(); - for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { - if (it != iter) { - break; - } - } - auto itBase = valueProducer->mCurrentBaseInfo.begin(); - for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) { - if (itBase != iterBase) { - break; - } - } - EXPECT_TRUE(it != iter); - EXPECT_TRUE(itBase != iterBase); - auto interval2 = it->second.intervals[0]; - auto baseInfo2 = itBase->second.baseInfos[0]; - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(4, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_FALSE(interval2.seenNewData); - - // next pull somehow did not happen, skip to end of bucket 3 - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - // Only one interval left. One was trimmed. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(5, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_FALSE(interval2.seenNewData); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); - - interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(14, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_FALSE(interval2.seenNewData); - ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); - auto iterator = valueProducer->mPastBuckets.begin(); - EXPECT_EQ(bucket4StartTimeNs, iterator->second[0].mBucketStartNs); - EXPECT_EQ(bucket5StartTimeNs, iterator->second[0].mBucketEndNs); - EXPECT_EQ(9, iterator->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); - iterator++; - EXPECT_EQ(bucketStartTimeNs, iterator->second[0].mBucketStartNs); - EXPECT_EQ(bucket2StartTimeNs, iterator->second[0].mBucketEndNs); - EXPECT_EQ(8, iterator->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); -} - -TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - // Used by onConditionChanged. - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo& curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - - vector<shared_ptr<LogEvent>> allData; - valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer->mHasGlobalBase); -} - -TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // Condition change to true. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })) - .WillOnce(Return(false)); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo& curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - valueProducer->onConditionChanged(false, bucketStartTimeNs + 20); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, valueProducer->mHasGlobalBase); -} - -TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50)); - return false; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to false. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Don't directly set mCondition; the real code never does that. Go through regular code path - // to avoid unexpected behaviors. - // valueProducer->mCondition = ConditionState::kTrue; - valueProducer->onConditionChanged(true, bucketStartTimeNs); - - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - - valueProducer->onConditionChanged(false, bucketStartTimeNs + 1); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer->mHasGlobalBase); -} - -TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(0); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 120)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Max delay is set to 0 so pull will exceed max delay. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); -} - -TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return()); - - ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, - protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, - bucket2StartTimeNs, bucket2StartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - valueProducer.mCondition = ConditionState::kFalse; - - // Event should be skipped since it is from previous bucket. - // Pull should not be called. - valueProducer.onConditionChanged(true, bucketStartTimeNs); - ASSERT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); -} - -TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - valueProducer->mHasGlobalBase = false; - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); - valueProducer->mHasGlobalBase = true; - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); -} - -/* - * Tests that a bucket is marked invalid when a condition change pull fails. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // First onConditionChanged - .WillOnce(Return(false)) - // Second onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kTrue); - - // Bucket start. - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - - // This will fail and should invalidate the whole bucket since we do not have all the data - // needed to compute the metric value when the screen was on. - valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); - - // Bucket end. - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); - - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - // Contains base from last pull which was successful. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(140, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 10, false /* include partial bucket */, true, - FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis()); -} - -/* - * Tests that a bucket is marked invalid when the guardrail is hit. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - metric.set_condition(StringToId("SCREEN_ON")); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - for (int i = 0; i < 2000; i++) { - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i)); - } - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 2); - EXPECT_EQ(true, valueProducer->mCurrentBucketIsSkipped); - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(0UL, valueProducer->mSkippedBuckets.size()); - - // Bucket 2 start. - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // First bucket added to mSkippedBuckets after flush. - ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size()); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */, - true, FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::DIMENSION_GUARDRAIL_REACHED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis()); -} - -/* - * Tests that a bucket is marked invalid when the bucket's initial pull fails. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120)); - return true; - })) - // Second onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kTrue); - - // Bucket start. - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); - - valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); - - // Bucket end. - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); - - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - // Contains base from last pull which was successful. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(140, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */, - true, FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis()); -} - -/* - * Tests that a bucket is marked invalid when the bucket's final pull fails - * (i.e. failed pull on bucket boundary). - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120)); - return true; - })) - // Second onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kTrue); - - // Bucket start. - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - - valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); - - // Bucket end. - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); - - valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); - - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - // Last pull failed so base has been reset. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer->mHasGlobalBase); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */, - true, FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis()); -} - -TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - // Start bucket. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - // Bucket 2 start. - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - - // Bucket 3 empty. - allData.clear(); - allData.push_back(CreateNoValuesLogEvent(tagId, bucket3StartTimeNs + 1)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // Data has been trimmed. - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); -} - -TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - // Empty pull. - valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer->mHasGlobalBase); -} - -TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 11); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 12); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 11); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 12); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - // End of bucket - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - // Data is empty, base should be reset. - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(5, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - metric.set_condition(StringToId("SCREEN_ON")); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - - // End of bucket - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 2)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // Key 1 should be reset since in not present in the most pull. - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - auto iterator = valueProducer->mCurrentSlicedBucket.begin(); - auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin(); - EXPECT_EQ(true, baseInfoIter->second.baseInfos[0].hasBase); - EXPECT_EQ(2, baseInfoIter->second.baseInfos[0].base.long_value); - EXPECT_EQ(false, iterator->second.intervals[0].hasValue); - iterator++; - baseInfoIter++; - EXPECT_EQ(false, baseInfoIter->second.baseInfos[0].hasBase); - EXPECT_EQ(1, baseInfoIter->second.baseInfos[0].base.long_value); - EXPECT_EQ(false, iterator->second.intervals[0].hasValue); - - EXPECT_EQ(true, valueProducer->mHasGlobalBase); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2; - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Initialization. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })) - // notifyAppUpgrade. - .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&, - const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10)); - return true; - })); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size()); - - switch (GetParam()) { - case APP_UPGRADE: - valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs); - EXPECT_EQ(0, valueProducer->getCurrentBucketNum()); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, - {partialBucketSplitTimeNs - bucketStartTimeNs}, - {bucketStartTimeNs}, {partialBucketSplitTimeNs}); - ASSERT_EQ(1UL, valueProducer->mCurrentFullBucket.size()); - - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4)); - // Pull fails and arrives late. - valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, - {partialBucketSplitTimeNs - bucketStartTimeNs}, - {bucketStartTimeNs}, {partialBucketSplitTimeNs}); - ASSERT_EQ(1, valueProducer->mSkippedBuckets.size()); - ASSERT_EQ(2, valueProducer->mSkippedBuckets[0].dropEvents.size()); - EXPECT_EQ(PULL_FAILED, valueProducer->mSkippedBuckets[0].dropEvents[0].reason); - EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[0].dropEvents[1].reason); - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs); - EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs); - ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size()); -} - -TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Second onConditionChanged. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5)); - return true; - })) - // Third onConditionChanged. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kUnknown); - - valueProducer->onConditionChanged(false, bucketStartTimeNs); - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - - // End of first bucket - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 4)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1); - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - - valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(5, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - - valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10); - - // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10}, - {bucket2StartTimeNs}, {bucket3StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_use_diff(false); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - // Initialization. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2; - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Initialization. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })) - // notifyAppUpgrade. - .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&, - const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - switch (GetParam()) { - case APP_UPGRADE: - valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - - // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // First on condition changed. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })) - // Second on condition changed. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 12); - - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(2, curInterval.value.long_value); - - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1); - - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}, {bucketStartTimeNs}, - {bucket2StartTimeNs}); -} - -// TODO: b/145705635 fix or delete this test -TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // First condition change. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })) - // 2nd condition change. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 8); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1)); - return true; - })) - // 3rd condition change. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); - - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 3, 10)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); - - valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8); - valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // There was not global base available so all buckets are invalid. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}); -} - -TEST(ValueMetricProducerTest, TestPullNeededFastDump) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return()); - - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - // Initial pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1)); - return true; - })); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - ProtoOutputStream output; - std::set<string> strSet; - valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true, - FAST, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - // Bucket is invalid since we did not pull when dump report was called. - ASSERT_EQ(0, report.value_metrics().data_size()); -} - -TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return()); - - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) - // Initial pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1)); - return true; - })); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, tagId, 2, 2)); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - ProtoOutputStream output; - std::set<string> strSet; - valueProducer.onDumpReport(bucket4StartTimeNs, false /* include recent buckets */, true, FAST, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - // Previous bucket is part of the report. - ASSERT_EQ(1, report.value_metrics().data_size()); - EXPECT_EQ(0, report.value_metrics().data(0).bucket_info(0).bucket_num()); -} - -TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp<EventMatcherWizard> eventMatcherWizard = - createEventMatcherWizard(tagId, logEventMatcherIndex); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return()); - - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Initial pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back( - CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10, tagId, 3, 3)); - return true; - })); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - ProtoOutputStream output; - std::set<string> strSet; - valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - ASSERT_EQ(1, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size()); - EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); -} - -TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_use_diff(false); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30); - - // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - metric.set_use_diff(false); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // condition becomes true - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - return true; - })) - // condition becomes false - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20)); - return true; - })); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 50); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(20, curInterval.value.long_value); - - // Now the alarm is delivered. Condition is off though. - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); -} - -TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - metric.set_use_diff(false); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _)) - // condition becomes true - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - return true; - })); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - - // Now the alarm is delivered. Condition is off though. - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; - ValueMetricProducer::BaseInfo curBaseInfo = - valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); -} - -TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - metric.set_use_diff(false); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Now the alarm is delivered. Condition is off though. - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // Condition was always false. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}); -} - -TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - metric.set_use_diff(false); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // condition becomes true - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - return true; - })) - .WillOnce(Return(false)); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 50); - - // Now the alarm is delivered. Condition is off though. - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // No buckets, we had a failure. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}); -} - -/* - * Test that DUMP_REPORT_REQUESTED dump reason is logged. - * - * For the bucket to be marked invalid during a dump report requested, - * three things must be true: - * - we want to include the current partial bucket - * - we need a pull (metric is pulled and condition is true) - * - the dump latency must be FAST - */ - -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 20); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 40, true /* include recent buckets */, true, - FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40), dropEvent.drop_time_millis()); -} - -/* - * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late condition - * change event (i.e. the condition change occurs in the wrong bucket). - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); - - // Bucket boundary pull. - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); - - // Late condition change event. - valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(1, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(1); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), dropEvent.drop_time_millis()); -} - -/* - * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late accumulate - * event (i.e. the accumulate events call occurs in the wrong bucket). - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 100); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); - - // Bucket boundary pull. - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 20)); - - // Late accumulateEvents event. - valueProducer->accumulateEvents(allData, bucket2StartTimeNs - 100, bucket2StartTimeNs - 100); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(1, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis()); -} - -/* - * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition - * when a metric is initialized. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10000); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kUnknown); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 10000; - valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that PULL_FAILED dump reason is logged due to a pull failure in - * #pullAndMatchEventsLocked. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10)); - return true; - })) - // Dump report requested, pull fails. - .WillOnce(Return(false)); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 10000; - valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that MULTIPLE_BUCKETS_SKIPPED dump reason is logged when a log event - * skips over more than one bucket. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket4StartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1000, 15)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - - // Condition change event that skips forward by three buckets. - valueProducer->onConditionChanged(false, bucket4StartTimeNs + 10); - - int64_t dumpTimeNs = bucket4StartTimeNs + 1000; - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(dumpTimeNs, true /* include current buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(2, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket4StartTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::MULTIPLE_BUCKETS_SKIPPED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket4StartTimeNs + 10), dropEvent.drop_time_millis()); - - // This bucket is skipped because a dumpReport with include current buckets is called. - // This creates a new bucket from bucket4StartTimeNs to dumpTimeNs in which we have no data - // since the condition is false for the entire bucket interval. - EXPECT_EQ(NanoToMillis(bucket4StartTimeNs), - report.value_metrics().skipped(1).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpTimeNs), - report.value_metrics().skipped(1).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size()); - - dropEvent = report.value_metrics().skipped(1).drop_event(0); - EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size - * is smaller than the "min_bucket_size_nanos" specified in the metric config. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - metric.set_min_bucket_size_nanos(10000000000); // 10 seconds - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 9000000); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 9000000, 15)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 9000000; - valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that NO_DATA dump reason is logged when a flushed bucket contains no data. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kFalse); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds - valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that all buckets are dropped due to condition unknown until the first onConditionChanged. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuckets) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 10)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucket2StartTimeNs + 15 * NS_PER_SEC, 15)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kUnknown); - - // Bucket should be dropped because of condition unknown. - int64_t appUpgradeTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC; - valueProducer->notifyAppUpgrade(appUpgradeTimeNs); - - // Bucket also dropped due to condition unknown - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 3)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // This bucket is also dropped due to condition unknown. - int64_t conditionChangeTimeNs = bucket2StartTimeNs + 10 * NS_PER_SEC; - valueProducer->onConditionChanged(true, conditionChangeTimeNs); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - int64_t dumpReportTimeNs = bucket2StartTimeNs + 15 * NS_PER_SEC; // 15 seconds - valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(3, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), dropEvent.drop_time_millis()); - - EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), - report.value_metrics().skipped(1).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(1).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size()); - - dropEvent = report.value_metrics().skipped(1).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis()); - - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(2).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(2).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(2).drop_event_size()); - - dropEvent = report.value_metrics().skipped(2).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(conditionChangeTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that a skipped bucket is logged when a forced bucket split occurs when the previous bucket - * was not flushed in time. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenForceBucketSplitBeforeBucketFlush) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10)); - return true; - })) - // App Update. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1000); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1000, 15)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition changed event - int64_t conditionChangeTimeNs = bucketStartTimeNs + 10; - valueProducer->onConditionChanged(true, conditionChangeTimeNs); - - // App update event. - int64_t appUpdateTimeNs = bucket2StartTimeNs + 1000; - valueProducer->notifyAppUpgrade(appUpdateTimeNs); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000; // 10 seconds - valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(1, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size()); - auto data = report.value_metrics().data(0); - ASSERT_EQ(0, data.bucket_info(0).bucket_num()); - EXPECT_EQ(5, data.bucket_info(0).values(0).value_long()); - - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(appUpdateTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test multiple bucket drop events in the same bucket. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kUnknown); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 1000; - valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true, - FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 10), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(1); - EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that the number of logged bucket drop events is capped at the maximum. - * The maximum is currently 10 and is set in MetricProducer::maxDropEventsReached(). - */ -TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // First condition change event. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - for (int i = 0; i < 2000; i++) { - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i)); - } - return true; - })) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 220); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 220, 10)); - return true; - })); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kUnknown); - - // First condition change event causes guardrail to be reached. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - - // 2-10 condition change events result in failed pulls. - valueProducer->onConditionChanged(false, bucketStartTimeNs + 30); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 70); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 90); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 100); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 150); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 170); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 190); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 200); - - // Condition change event 11 - valueProducer->onConditionChanged(true, bucketStartTimeNs + 220); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 1000; - // Because we already have 10 dump events in the current bucket, - // this case should not be added to the list of dump events. - valueProducer->onDumpReport(bucketStartTimeNs + 1000, true /* include recent buckets */, true, - FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(10, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 10), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(1); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 30), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(2); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 50), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(3); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 70), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(4); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 90), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(5); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 100), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(6); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 150), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(7); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 170), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(8); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 190), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(9); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 200), dropEvent.drop_time_millis()); -} - -/* - * Test metric with a simple sliced state - * - Increasing values - * - Using diff - * - Second field is value field - */ -TEST(ValueMetricProducerTest, TestSlicedState) { - // Set up ValueMetricProducer. - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE"); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // ValueMetricProducer initialized. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })) - // Screen state change to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5)); - return true; - })) - // Screen state change to OFF. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 9)); - return true; - })) - // Screen state change to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30)); - return true; - })); - - StateManager::getInstance().clear(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithState( - pullerManager, metric, {util::SCREEN_STATE_CHANGED}, {}); - EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - - // Bucket status after metric initialized. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); - - // Bucket status after screen state change kStateUnknown->ON. - auto screenEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, ON} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(0, it->second.intervals.size()); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC); - // Value for dimension, state key {{}, kStateUnknown} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(2, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, - bucketStartTimeNs + 5 * NS_PER_SEC); - - // Bucket status after screen state change ON->OFF. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, OFF} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(0, it->second.intervals.size()); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); - // Value for dimension, state key {{}, ON} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(4, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, - bucketStartTimeNs + 10 * NS_PER_SEC); - // Value for dimension, state key {{}, kStateUnknown} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(2, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, - bucketStartTimeNs + 5 * NS_PER_SEC); - - // Bucket status after screen state change OFF->ON. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, OFF} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(12, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, - bucketStartTimeNs + 15 * NS_PER_SEC); - // Value for dimension, state key {{}, ON} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(4, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, true, 5 * NS_PER_SEC, - bucketStartTimeNs + 15 * NS_PER_SEC); - // Value for dimension, state key {{}, kStateUnknown} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(2, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, - bucketStartTimeNs + 5 * NS_PER_SEC); - - // Start dump report and check output. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC, - true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(3, report.value_metrics().data_size()); - - // {{}, kStateUnknown} - auto data = report.value_metrics().data(0); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); - 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(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {{}, ON} - data = report.value_metrics().data(1); - ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); - EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); - 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(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {{}, OFF} - data = report.value_metrics().data(2); - ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size()); - EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); - 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(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); -} - -/* - * Test metric with sliced state with map - * - Increasing values - * - Using diff - * - Second field is value field - */ -TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { - // Set up ValueMetricProducer. - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF"); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // ValueMetricProducer initialized. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })) - // Screen state change to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5)); - return true; - })) - // Screen state change to VR has no pull because it is in the same - // state group as ON. - - // Screen state change to ON has no pull because it is in the same - // state group as VR. - - // Screen state change to OFF. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30)); - return true; - })); - - const StateMap& stateMap = - CreateScreenStateOnOffMap(/*screen on id=*/321, /*screen off id=*/123); - const StateMap_StateGroup screenOnGroup = stateMap.group(0); - const StateMap_StateGroup screenOffGroup = stateMap.group(1); - - unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; - for (auto group : stateMap.group()) { - for (auto value : group.value()) { - stateGroupMap[SCREEN_STATE_ATOM_ID][value] = group.group_id(); - } - } - - StateManager::getInstance().clear(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithState( - pullerManager, metric, {util::SCREEN_STATE_CHANGED}, stateGroupMap); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - - // Bucket status after metric initialized. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, {kStateUnknown}} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); - - // Bucket status after screen state change kStateUnknown->ON. - auto screenEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - itBase->second.currentState.getValues()[0].mValue.long_value); - // Value for dimension, state key {{}, ON GROUP} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC); - // Value for dimension, state key {{}, kStateUnknown} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(2, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, - bucketStartTimeNs + 5 * NS_PER_SEC); - - // Bucket status after screen state change ON->VR. - // Both ON and VR are in the same state group, so the base should not change. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_VR); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, ON GROUP} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC); - // Value for dimension, state key {{}, kStateUnknown} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(2, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, - bucketStartTimeNs + 5 * NS_PER_SEC); - - // Bucket status after screen state change VR->ON. - // Both ON and VR are in the same state group, so the base should not change. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, ON GROUP} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC); - // Value for dimension, state key {{}, kStateUnknown} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(2, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, - bucketStartTimeNs + 5 * NS_PER_SEC); - - // Bucket status after screen state change VR->OFF. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(screenOffGroup.group_id(), - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, OFF GROUP} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(screenOffGroup.group_id(), - it->first.getStateValuesKey().getValues()[0].mValue.long_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 15 * NS_PER_SEC); - // Value for dimension, state key {{}, ON GROUP} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - it->first.getStateValuesKey().getValues()[0].mValue.long_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(16, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 15 * NS_PER_SEC); - // Value for dimension, state key {{}, kStateUnknown} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(2, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, - bucketStartTimeNs + 5 * NS_PER_SEC); - - // Start dump report and check output. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC, - true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(3, report.value_metrics().data_size()); - - // {{}, kStateUnknown} - auto data = report.value_metrics().data(0); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); - 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(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {{}, ON GROUP} - data = report.value_metrics().data(1); - ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); - EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); - 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(screenOnGroup.group_id(), data.slice_by_state(0).group_id()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {{}, OFF GROUP} - data = report.value_metrics().data(2); - ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size()); - EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); - 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(screenOffGroup.group_id(), data.slice_by_state(0).group_id()); - EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); -} - -/* - * Test metric that slices by state with a primary field and has dimensions - * - Increasing values - * - Using diff - * - Second field is value field - */ -TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { - // Set up ValueMetricProducer. - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE"); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - - MetricStateLink* stateLink = metric.add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - - /* - NOTE: "1" denotes uid 1 and "2" denotes uid 2. - bucket # 1 bucket # 2 - 10 20 30 40 50 60 70 80 90 100 110 120 (seconds) - |------------------------------------------|---------------------------------|-- - - (kStateUnknown) - 1 - |-------------| - 20 - - 2 - |----------------------------| - 40 - - (FOREGROUND) - 1 1 - |----------------------------|-------------| |------| - 40 20 10 - - - (BACKGROUND) - 1 - |------------| - 20 - 2 - |-------------|---------------------------------| - 20 50 - */ - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // ValueMetricProducer initialized. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 7)); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1 /*uid*/, 3)); - return true; - })) - // Uid 1 process state change from kStateUnknown -> Foreground - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, - 1 /*uid*/, 6)); - - // This event should be skipped. - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, - 2 /*uid*/, 8)); - return true; - })) - // Uid 2 process state change from kStateUnknown -> Background - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, - 2 /*uid*/, 9)); - - // This event should be skipped. - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, - 1 /*uid*/, 12)); - return true; - })) - // Uid 1 process state change from Foreground -> Background - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20 * NS_PER_SEC); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC, - 1 /*uid*/, 13)); - - // This event should be skipped. - data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC, - 2 /*uid*/, 11)); - return true; - })) - // Uid 1 process state change from Background -> Foreground - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40 * NS_PER_SEC); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC, - 1 /*uid*/, 17)); - - // This event should be skipped. - data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC, - 2 /*uid */, 15)); - return true; - })) - // Dump report pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, - 2 /*uid*/, 20)); - data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, - 1 /*uid*/, 21)); - return true; - })); - - StateManager::getInstance().clear(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithState( - pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); - - // Bucket status after metric initialized. - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {uid 1}. - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{uid 1}, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); - // Base for dimension key {uid 2} - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{uid 2}, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); - - // Bucket status after uid 1 process state change kStateUnknown -> Foreground. - auto uidProcessEvent = - CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */, - android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {uid 1}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 1, kStateUnknown}. - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(3, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - // Value for key {uid 1, FOREGROUND}. - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Base for dimension key {uid 2}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, kStateUnknown}. - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); - - // Bucket status after uid 2 process state change kStateUnknown -> Background. - uidProcessEvent = - CreateUidProcessStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, 2 /* uid */, - android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {uid 2}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, BACKGROUND}. - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 40 * NS_PER_SEC); - - // Base for dimension key {uid 1}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 1, kStateUnknown}. - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(3, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {uid 1, FOREGROUND}. - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {uid 2, kStateUnknown} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(2, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 40 * NS_PER_SEC, - bucketStartTimeNs + 40 * NS_PER_SEC); - - // Pull at end of first bucket. - vector<shared_ptr<LogEvent>> allData; - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 10)); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 15)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); - - // Buckets flushed after end of first bucket. - // None of the buckets should have a value. - ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(4UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); - // Base for dimension key {uid 2}. - it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, BACKGROUND}. - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); - EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); - - // Base for dimension key {uid 1} - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(10, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 1, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* kStateTracker::kUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); - - // Value for key {uid 1, FOREGROUND} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); - EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); - - // Value for key {uid 2, kStateUnknown} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* kStateTracker::kUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC); - EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); - - // Bucket status after uid 1 process state change from Foreground -> Background. - uidProcessEvent = - CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, 1 /* uid */, - android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - - ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(4UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); - // Base for dimension key {uid 2}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, BACKGROUND}. - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); - - // Base for dimension key {uid 1} - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(13, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 1, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {uid 1, BACKGROUND} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 20 * NS_PER_SEC); - - // Value for key {uid 1, FOREGROUND} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(3, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, - bucket2StartTimeNs + 20 * NS_PER_SEC); - - // Value for key {uid 2, kStateUnknown} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC); - - // Bucket status after uid 1 process state change Background->Foreground. - uidProcessEvent = - CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 40 * NS_PER_SEC, 1 /* uid */, - android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - - ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); - // Base for dimension key {uid 2} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, BACKGROUND} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); - - // Base for dimension key {uid 1} - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(17, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 1, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {uid 1, BACKGROUND} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(4, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, - bucket2StartTimeNs + 40 * NS_PER_SEC); - - // Value for key {uid 1, FOREGROUND} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(3, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC, - bucket2StartTimeNs + 40 * NS_PER_SEC); - - // Value for key {uid 2, kStateUnknown} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC); - - // Start dump report and check output. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, - true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(5, report.value_metrics().data_size()); - - // {uid 1, BACKGROUND} - auto data = report.value_metrics().data(0); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); - 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::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {uid 2, kStateUnknown} - data = report.value_metrics().data(1); - ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); - EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); - 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(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {uid 1, FOREGROUND} - data = report.value_metrics().data(2); - 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::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(2, report.value_metrics().data(2).bucket_info_size()); - EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); - EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long()); - EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); - - // {uid 1, kStateUnknown} - data = report.value_metrics().data(3); - ASSERT_EQ(1, report.value_metrics().data(3).bucket_info_size()); - EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long()); - 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(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {uid 2, BACKGROUND} - data = report.value_metrics().data(4); - 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::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(2, report.value_metrics().data(4).bucket_info_size()); - EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long()); - EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); -} - -/* - * Test slicing condition_true_nanos by state for metric that slices by state when data is not - * present in pulled data during a state change. - */ -TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange) { - // Set up ValueMetricProducer. - ValueMetric metric = - ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE"); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - /* - NOTE: "-" means that the data was not present in the pulled data. - - bucket # 1 - 10 20 30 40 50 60 (seconds) - |-------------------------------------------------------|-- - x (kStateUnknown) - |-----------| - 10 - - x x (ON) - |---------------------| |-----------| - 20 10 - - - (OFF) - */ - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // ValueMetricProducer initialized. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })) - // Battery saver mode state changed to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5)); - return true; - })) - // Battery saver mode state changed to OFF but data for dimension key {} is not present - // in the pulled data. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC); - data->clear(); - return true; - })) - // Battery saver mode state changed to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 7)); - return true; - })) - // Dump report pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15)); - return true; - })); - - StateManager::getInstance().clear(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithState( - pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}); - EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, - valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount( - util::BATTERY_SAVER_MODE_STATE_CHANGED)); - - // Bucket status after metric initialized. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); - - // Bucket status after battery saver mode ON event. - unique_ptr<LogEvent> batterySaverOnEvent = - CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOnEvent); - - // Base for dimension key {} - - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, ON} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); - - // Value for key {{}, -1} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 10 * NS_PER_SEC); - - // Bucket status after battery saver mode OFF event which is not present - // in the pulled data. - unique_ptr<LogEvent> batterySaverOffEvent = - CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOffEvent); - - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_FALSE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, ON} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, - bucketStartTimeNs + 30 * NS_PER_SEC); - - // Value for key {{}, -1} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 10 * NS_PER_SEC); - - // Bucket status after battery saver mode ON event. - batterySaverOnEvent = - CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 40 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOnEvent); - - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, ON} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC, - bucketStartTimeNs + 40 * NS_PER_SEC); - - // Value for key {{}, -1} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 10 * NS_PER_SEC); - - // Start dump report and check output. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC, - true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(2, report.value_metrics().data_size()); - - // {{}, kStateUnknown} - ValueMetricData data = report.value_metrics().data(0); - EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {{}, ON} - data = report.value_metrics().data(1); - EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); -} - -/* - * Test for metric that slices by state when data is not present in pulled data - * during an event and then a flush occurs for the current bucket. With the new - * condition timer behavior, a "new" MetricDimensionKey is inserted into - * `mCurrentSlicedBucket` before intervals are closed/added to that new - * MetricDimensionKey. - */ -TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket) { - // Set up ValueMetricProducer. - ValueMetric metric = - ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE"); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - /* - NOTE: "-" means that the data was not present in the pulled data. - - bucket # 1 - 10 20 30 40 50 60 (seconds) - |-------------------------------------------------------|-- - - (kStateUnknown) - - - (ON) - */ - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // ValueMetricProducer initialized but data for dimension key {} is not present - // in the pulled data.. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - return true; - })) - // Battery saver mode state changed to ON but data for dimension key {} is not present - // in the pulled data. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); - data->clear(); - return true; - })) - // Dump report pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15)); - return true; - })); - - StateManager::getInstance().clear(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithState( - pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}); - EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, - valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount( - util::BATTERY_SAVER_MODE_STATE_CHANGED)); - - // Bucket status after metric initialized. - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size()); - - // Bucket status after battery saver mode ON event which is not present - // in the pulled data. - unique_ptr<LogEvent> batterySaverOnEvent = - CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOnEvent); - - ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - - // Start dump report and check output. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC, - true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); -} - -TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary) { - // Set up ValueMetricProducer. - ValueMetric metric = - ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE"); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - /* - bucket # 1 bucket # 2 - 10 20 30 40 50 60 70 80 90 100 110 120 (seconds) - |------------------------------------|---------------------------|-- - x (kStateUnknown) - |-----| - 10 - x x (ON) - |-----| |-----------| - 10 20 - x (OFF) - |------------------------| - 40 - */ - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // ValueMetricProducer initialized. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })) - // Battery saver mode state changed to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5)); - return true; - })) - // Battery saver mode state changed to OFF. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 7)); - return true; - })) - // Battery saver mode state changed to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 10)); - return true; - })) - // Dump report pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 15)); - return true; - })); - - StateManager::getInstance().clear(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithState( - pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}); - EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, - valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount( - util::BATTERY_SAVER_MODE_STATE_CHANGED)); - - // Bucket status after metric initialized. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); - - // Bucket status after battery saver mode ON event. - unique_ptr<LogEvent> batterySaverOnEvent = - CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOnEvent); - - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, ON} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); - - // Value for key {{}, -1} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 10 * NS_PER_SEC); - - // Bucket status after battery saver mode OFF event. - unique_ptr<LogEvent> batterySaverOffEvent = - CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 20 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOffEvent); - - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, OFF} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{}, ON} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{}, -1} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 10 * NS_PER_SEC); - - // Bucket status after battery saver mode ON event. - batterySaverOnEvent = - CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOnEvent); - - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, OFF} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC, - bucket2StartTimeNs + 30 * NS_PER_SEC); - - // Value for key {{}, ON} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC); - - // Value for key {{}, -1} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 10 * NS_PER_SEC); - - // Start dump report and check output. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, - true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(3, report.value_metrics().data_size()); - - // {{}, kStateUnknown} - ValueMetricData data = report.value_metrics().data(0); - EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {{}, ON} - data = report.value_metrics().data(1); - EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); - - // {{}, OFF} - data = report.value_metrics().data(2); - EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); -} - -/* - * Test slicing condition_true_nanos by state for metric that slices by state when data is not - * present in pulled data during a condition change. - */ -TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange) { - // Set up ValueMetricProducer. - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState( - "BATTERY_SAVER_MODE_STATE"); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - /* - NOTE: "-" means that the data was not present in the pulled data. - - bucket # 1 - 10 20 30 40 50 60 (seconds) - |-------------------------------------------------------|-- - - T F T (Condition) - x (ON) - |----------------------| - - 20 - */ - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Battery saver mode state changed to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 3)); - return true; - })) - // Condition changed to false. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5)); - return true; - })) - // Condition changed to true but data for dimension key {} is not present in the - // pulled data. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC); - data->clear(); - return true; - })) - // Dump report pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 20)); - return true; - })); - - StateManager::getInstance().clear(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithConditionAndState( - pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}, - ConditionState::kTrue); - EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, - valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount( - util::BATTERY_SAVER_MODE_STATE_CHANGED)); - - // Bucket status after battery saver mode ON event. - unique_ptr<LogEvent> batterySaverOnEvent = - CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOnEvent); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, ON} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); - - // Value for key {{}, -1} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Bucket status after condition change to false. - valueProducer->onConditionChanged(false, bucketStartTimeNs + 30 * NS_PER_SEC); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, ON} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, - bucketStartTimeNs + 30 * NS_PER_SEC); - - // Value for key {{}, -1} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Bucket status after condition change to true. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 40 * NS_PER_SEC); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_FALSE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, ON} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, - bucketStartTimeNs + 30 * NS_PER_SEC); - - // Value for key {{}, -1} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Start dump report and check output. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC, - true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(1, report.value_metrics().data_size()); - - // {{}, ON} - ValueMetricData data = report.value_metrics().data(0); - EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); -} - -/* - * Test slicing condition_true_nanos by state for metric that slices by state with a primary field, - * condition, and has multiple dimensions. - */ -TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) { - // Set up ValueMetricProducer. - ValueMetric metric = - ValueMetricProducerTestHelper::createMetricWithConditionAndState("UID_PROCESS_STATE"); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - metric.mutable_dimensions_in_what()->add_child()->set_field(3); - - MetricStateLink* stateLink = metric.add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - - /* - bucket # 1 bucket # 2 - 10 20 30 40 50 60 70 80 90 100 110 120 (seconds) - |------------------------------------------|---------------------------------|-- - - T F T (Condition) - (FOREGROUND) - x {1, 14} - |------| - 10 - - x {1, 16} - |------| - 10 - x {2, 8} - |-------------| - 20 - - (BACKGROUND) - x {1, 14} - |-------------| |----------|---------------------------------| - 20 15 50 - - x {1, 16} - |-------------| |----------|---------------------------------| - 20 15 50 - - x {2, 8} - |----------| |----------|-------------------| - 15 15 30 - */ - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Uid 1 process state change from kStateUnknown -> Foreground - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, - 1 /*uid*/, 3, 14 /*tag*/)); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, - 1 /*uid*/, 3, 16 /*tag*/)); - - // This event should be skipped. - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, - 2 /*uid*/, 5, 8 /*tag*/)); - return true; - })) - // Uid 1 process state change from Foreground -> Background - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC); - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, - 1 /*uid*/, 5, 14 /*tag*/)); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, - 1 /*uid*/, 5, 16 /*tag*/)); - - // This event should be skipped. - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, - 2 /*uid*/, 7, 8 /*tag*/)); - - return true; - })) - // Uid 2 process state change from kStateUnknown -> Background - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC); - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC, - 2 /*uid*/, 9, 8 /*tag*/)); - - // This event should be skipped. - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC, - 1 /*uid*/, 9, 14 /* tag */)); - - // This event should be skipped. - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC, - 1 /*uid*/, 9, 16 /* tag */)); - - return true; - })) - // Condition changed to false. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC); - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, - 1 /*uid*/, 11, 14 /* tag */)); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, - 1 /*uid*/, 11, 16 /* tag */)); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, - 2 /*uid*/, 11, 8 /*tag*/)); - - return true; - })) - // Condition changed to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC); - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC, - 1 /*uid*/, 13, 14 /* tag */)); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC, - 1 /*uid*/, 13, 16 /* tag */)); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC, - 2 /*uid*/, 13, 8 /*tag*/)); - return true; - })) - // Uid 2 process state change from Background -> Foreground - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC); - data->clear(); - data->push_back(CreateThreeValueLogEvent( - tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/)); - - // This event should be skipped. - data->push_back(CreateThreeValueLogEvent( - tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */)); - // This event should be skipped. - data->push_back(CreateThreeValueLogEvent( - tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */)); - - return true; - })) - // Dump report pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC); - data->clear(); - data->push_back(CreateThreeValueLogEvent( - tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 14 /* tag */)); - data->push_back(CreateThreeValueLogEvent( - tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 16 /* tag */)); - data->push_back(CreateThreeValueLogEvent( - tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 21, 8 /*tag*/)); - return true; - })); - - StateManager::getInstance().clear(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithConditionAndState( - pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}, ConditionState::kTrue); - EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); - - // Condition is true. - // Bucket status after uid 1 process state change kStateUnknown -> Foreground. - auto uidProcessEvent = - CreateUidProcessStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, 1 /* uid */, - android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension {uid 1, tag 16}. - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, uid 16}, FOREGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); - // Value for key {{uid 1, tag 16}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Base for dimension key {uid 1, tag 14}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, tag 14}, FOREGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); - // Value for key {{uid 1, tag 14}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Bucket status after uid 1 process state change Foreground -> Background. - uidProcessEvent = - CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */, - android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(6UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension {uid 1, tag 16}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, uid 16}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Base for dimension key {uid 1, tag 14}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, tag 14}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, uid 16}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, tag 16}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Value for key {{uid 1, tag 14}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, tag 14}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Bucket status after uid 2 process state change kStateUnknown -> Background. - uidProcessEvent = - CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */, - android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension {uid 2, tag 8}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 2, uid 8}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 25 * NS_PER_SEC); - - // Value for key {{uid 2, uid 8}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Base for dimension {uid 1, tag 16}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, uid 16}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Base for dimension key {uid 1, tag 14}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, tag 14}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, uid 16}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, tag 16}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Value for key {{uid 1, tag 14}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, tag 14}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Bucket 1 status after condition change to false. - // All condition timers should be turned off. - valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC); - ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension {uid 2, tag 8}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 2, uid 8}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 15 * NS_PER_SEC, - bucketStartTimeNs + 40 * NS_PER_SEC); - - // Value for key {{uid 2, uid 8}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Base for dimension {uid 1, tag 16}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, uid 16}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, - bucketStartTimeNs + 40 * NS_PER_SEC); - - // Base for dimension key {uid 1, tag 14}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, tag 14}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, - bucketStartTimeNs + 40 * NS_PER_SEC); - - // Value for key {{uid 1, uid 16}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, tag 16}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Value for key {{uid 1, tag 14}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, tag 14}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Bucket 1 status after condition change to true. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 45 * NS_PER_SEC); - ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size()); - ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension {uid 2, tag 8}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 2, uid 8}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 15 * NS_PER_SEC, - bucketStartTimeNs + 45 * NS_PER_SEC); - - // Value for key {{uid 2, uid 8}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Base for dimension {uid 1, tag 16}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, uid 16}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC, - bucketStartTimeNs + 45 * NS_PER_SEC); - - // Base for dimension key {uid 1, tag 14}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, tag 14}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC, - bucketStartTimeNs + 45 * NS_PER_SEC); - - // Value for key {{uid 1, uid 16}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, tag 16}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Value for key {{uid 1, tag 14}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, tag 14}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Pull at end of first bucket. - vector<shared_ptr<LogEvent>> allData; - allData.push_back( - CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */)); - allData.push_back( - CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */)); - allData.push_back( - CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); - - // Buckets flushed after end of first bucket. - // All condition timers' behavior should rollover to bucket 2. - ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(5UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size()); - // Base for dimension {uid 2, tag 8}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 2, uid 8}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); - ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size()); - EXPECT_EQ(30 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); - - // Value for key {{uid 2, uid 8}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Base for dimension {uid 1, tag 16}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, uid 16}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); - ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size()); - EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); - - // Base for dimension key {uid 1, tag 14}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, tag 14}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); - ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size()); - EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); - - // Value for key {{uid 1, uid 16}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size()); - EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); - - // Value for key {{uid 1, tag 16}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Value for key {{uid 1, tag 14}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size()); - EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); - - // Value for key {{uid 1, tag 14}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Bucket 2 status after uid 2 process state change Background->Foreground. - uidProcessEvent = - CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */, - android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - - ASSERT_EQ(9UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size()); - // Base for dimension {uid 2, tag 8}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 2, uid 8}, FOREGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC); - - // Value for key {{uid 2, uid 8}, BACKGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC, - bucket2StartTimeNs + 30 * NS_PER_SEC); - - // Value for key {{uid 2, uid 8}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Base for dimension {uid 1, tag 16}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, uid 16}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); - - // Base for dimension key {uid 1, tag 14}. - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{uid 1, tag 14}, BACKGROUND}. - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); - - // Value for key {{uid 1, uid 16}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, tag 16}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Value for key {{uid 1, tag 14}, FOREGROUND}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - - // Value for key {{uid 1, tag 14}, kStateUnknown}. - it++; - ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Start dump report and check output. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, - true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(6, report.value_metrics().data_size()); - - // {{uid 1, tag 14}, FOREGROUND}. - auto data = report.value_metrics().data(0); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {{uid 1, tag 16}, BACKGROUND}. - data = report.value_metrics().data(1); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); - - // {{uid 1, tag 14}, BACKGROUND}. - data = report.value_metrics().data(2); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); - - // {{uid 1, tag 16}, FOREGROUND}. - data = report.value_metrics().data(3); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {{uid 2, tag 8}, FOREGROUND}. - data = report.value_metrics().data(4); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - // {{uid 2, tag 8}, BACKGROUND}. - data = report.value_metrics().data(5); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); -} - -TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { - // Set up ValueMetricProducer. - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState( - "BATTERY_SAVER_MODE_STATE"); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) - // Condition changed to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 3)); - return true; - })) - // Battery saver mode state changed to OFF. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5)); - return true; - })) - // Condition changed to false. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 15)); - return true; - })); - - StateManager::getInstance().clear(); - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithConditionAndState( - pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}, - ConditionState::kFalse); - EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, - valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount( - util::BATTERY_SAVER_MODE_STATE_CHANGED)); - - // Bucket status after battery saver mode ON event. - // Condition is false so we do nothing. - unique_ptr<LogEvent> batterySaverOnEvent = - CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOnEvent); - EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(0UL, valueProducer->mCurrentBaseInfo.size()); - - // Bucket status after condition change to true. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - std::unordered_map<HashableDimensionKey, ValueMetricProducer::DimensionsInWhatInfo>::iterator - itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, ON} - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it = - valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - // Value for key {{}, -1} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Bucket status after battery saver mode OFF event. - unique_ptr<LogEvent> batterySaverOffEvent = - CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOffEvent); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, OFF} - ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 30 * NS_PER_SEC); - // Value for key {{}, ON} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(2, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucketStartTimeNs + 30 * NS_PER_SEC); - // Value for key {{}, -1} - it++; - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Pull at end of first bucket. - vector<shared_ptr<LogEvent>> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(11, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, OFF} - it = valueProducer->mCurrentSlicedBucket.begin(); - assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); - // Value for key {{}, ON} - it++; - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC); - // Value for key {{}, -1} - it++; - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Bucket 2 status after condition change to false. - valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_FALSE(itBase->second.baseInfos[0].hasBase); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, OFF} - ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second.intervals[0].hasValue); - EXPECT_EQ(4, it->second.intervals[0].value.long_value); - assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, - bucket2StartTimeNs + 10 * NS_PER_SEC); - // Value for key {{}, ON} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second.intervals[0].hasValue); - assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC); - // Value for key {{}, -1} - it++; - assertConditionTimer(it->second.conditionTimer, false, 0, 0); - - // Start dump report and check output. - ProtoOutputStream output; - std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, - true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(2, report.value_metrics().data_size()); - - ValueMetricData data = report.value_metrics().data(0); - EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).values(0).value_long()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - - data = report.value_metrics().data(1); - EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(6, data.bucket_info(0).values(0).value_long()); - EXPECT_EQ(4, data.bucket_info(1).values(0).value_long()); - EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); -} - -/* - * Test bucket splits when condition is unknown. - */ -TEST(ValueMetricProducerTest, TestForcedBucketSplitWhenConditionUnknownSkipsBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - - sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, - ConditionState::kUnknown); - - // App update event. - int64_t appUpdateTimeNs = bucketStartTimeNs + 1000; - valueProducer->notifyAppUpgrade(appUpdateTimeNs); - - // Check dump report. - ProtoOutputStream output; - std::set<string> strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds - valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(appUpdateTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp deleted file mode 100644 index 108df04b45cb..000000000000 --- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp +++ /dev/null @@ -1,57 +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. - -#include "metrics_test_helper.h" - -namespace android { -namespace os { -namespace statsd { - -HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) { - HashableDimensionKey dimension; - int pos[] = {key, 0, 0}; - dimension.addValue(FieldValue(Field(tagId, pos, 0), Value(value))); - - return dimension; -} - -HashableDimensionKey getMockedDimensionKeyLongValue(int tagId, int key, int64_t value) { - HashableDimensionKey dimension; - int pos[] = {key, 0, 0}; - dimension.addValue(FieldValue(Field(tagId, pos, 0), Value(value))); - - return dimension; -} - -MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) { - return MetricDimensionKey(getMockedDimensionKey(tagId, key, value), DEFAULT_DIMENSION_KEY); -} - -MetricDimensionKey getMockedStateDimensionKey(int tagId, int key, int64_t value) { - return MetricDimensionKey(DEFAULT_DIMENSION_KEY, - getMockedDimensionKeyLongValue(tagId, key, value)); -} - -void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) { - matcher->set_field(tagId); -} - -void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatcher* matcher) { - matcher->set_field(tagId); - matcher->add_child()->set_field(fieldNum); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h deleted file mode 100644 index 39232c194ada..000000000000 --- a/cmds/statsd/tests/metrics/metrics_test_helper.h +++ /dev/null @@ -1,63 +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. -#pragma once - -#include "src/condition/ConditionWizard.h" -#include "src/external/StatsPullerManager.h" -#include "src/packages/UidMap.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -namespace android { -namespace os { -namespace statsd { - -class MockConditionWizard : public ConditionWizard { -public: - MOCK_METHOD3(query, - ConditionState(const int conditionIndex, const ConditionKey& conditionParameters, - const bool isPartialLink)); -}; - -class MockStatsPullerManager : public StatsPullerManager { -public: - MOCK_METHOD5(RegisterReceiver, - void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver, - int64_t nextPulltimeNs, int64_t intervalNs)); - MOCK_METHOD3(UnRegisterReceiver, - void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver)); - MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data)); - MOCK_METHOD4(Pull, bool(const int pullCode, const vector<int32_t>& uids, - const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data)); - MOCK_METHOD2(RegisterPullUidProvider, - void(const ConfigKey& configKey, wp<PullUidProvider> provider)); - MOCK_METHOD2(UnregisterPullUidProvider, - void(const ConfigKey& configKey, wp<PullUidProvider> provider)); -}; - -HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value); -MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value); - -HashableDimensionKey getMockedDimensionKeyLongValue(int tagId, int key, int64_t value); -MetricDimensionKey getMockedStateDimensionKey(int tagId, int key, int64_t value); - -// Utils to build FieldMatcher proto for simple one-depth atoms. -void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher); -void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp deleted file mode 100644 index d78c14c6ed82..000000000000 --- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp +++ /dev/null @@ -1,3632 +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. - -#include "src/metrics/parsing_utils/config_update_utils.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <private/android_filesystem_config.h> -#include <stdio.h> - -#include <set> -#include <unordered_map> -#include <vector> - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "src/condition/CombinationConditionTracker.h" -#include "src/condition/SimpleConditionTracker.h" -#include "src/matchers/CombinationAtomMatchingTracker.h" -#include "src/metrics/DurationMetricProducer.h" -#include "src/metrics/GaugeMetricProducer.h" -#include "src/metrics/ValueMetricProducer.h" -#include "src/metrics/parsing_utils/metrics_manager_util.h" -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using android::os::statsd::Predicate; -using std::map; -using std::nullopt; -using std::optional; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -namespace { - -ConfigKey key(123, 456); -const int64_t timeBaseNs = 1000 * NS_PER_SEC; - -sp<UidMap> uidMap = new UidMap(); -sp<StatsPullerManager> pullerManager = new StatsPullerManager(); -sp<AlarmMonitor> anomalyAlarmMonitor; -sp<AlarmMonitor> periodicAlarmMonitor = new AlarmMonitor( - /*minDiffToUpdateRegisteredAlarmTimeSec=*/0, - [](const shared_ptr<IStatsCompanionService>&, int64_t) {}, - [](const shared_ptr<IStatsCompanionService>&) {}); -set<int> allTagIds; -vector<sp<AtomMatchingTracker>> oldAtomMatchingTrackers; -unordered_map<int64_t, int> oldAtomMatchingTrackerMap; -vector<sp<ConditionTracker>> oldConditionTrackers; -unordered_map<int64_t, int> oldConditionTrackerMap; -vector<sp<MetricProducer>> oldMetricProducers; -unordered_map<int64_t, int> oldMetricProducerMap; -std::vector<sp<AnomalyTracker>> oldAnomalyTrackers; -unordered_map<int64_t, int> oldAlertTrackerMap; -std::vector<sp<AlarmTracker>> oldAlarmTrackers; -unordered_map<int, std::vector<int>> tmpConditionToMetricMap; -unordered_map<int, std::vector<int>> tmpTrackerToMetricMap; -unordered_map<int, std::vector<int>> tmpTrackerToConditionMap; -unordered_map<int, std::vector<int>> tmpActivationAtomTrackerToMetricMap; -unordered_map<int, std::vector<int>> tmpDeactivationAtomTrackerToMetricMap; -vector<int> metricsWithActivation; -map<int64_t, uint64_t> oldStateHashes; -std::set<int64_t> noReportMetricIds; - -class ConfigUpdateTest : public ::testing::Test { -public: - ConfigUpdateTest() { - } - - void SetUp() override { - allTagIds.clear(); - oldAtomMatchingTrackers.clear(); - oldAtomMatchingTrackerMap.clear(); - oldConditionTrackers.clear(); - oldConditionTrackerMap.clear(); - oldMetricProducers.clear(); - oldMetricProducerMap.clear(); - oldAnomalyTrackers.clear(); - oldAlarmTrackers.clear(); - tmpConditionToMetricMap.clear(); - tmpTrackerToMetricMap.clear(); - tmpTrackerToConditionMap.clear(); - tmpActivationAtomTrackerToMetricMap.clear(); - tmpDeactivationAtomTrackerToMetricMap.clear(); - oldAlertTrackerMap.clear(); - metricsWithActivation.clear(); - oldStateHashes.clear(); - noReportMetricIds.clear(); - StateManager::getInstance().clear(); - } -}; - -bool initConfig(const StatsdConfig& config) { - return initStatsdConfig( - key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap, - oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldMetricProducerMap, - oldAnomalyTrackers, oldAlarmTrackers, tmpConditionToMetricMap, tmpTrackerToMetricMap, - tmpTrackerToConditionMap, tmpActivationAtomTrackerToMetricMap, - tmpDeactivationAtomTrackerToMetricMap, oldAlertTrackerMap, metricsWithActivation, - oldStateHashes, noReportMetricIds); -} - -EventMetric createEventMetric(string name, int64_t what, optional<int64_t> condition) { - EventMetric metric; - metric.set_id(StringToId(name)); - metric.set_what(what); - if (condition) { - metric.set_condition(condition.value()); - } - return metric; -} - -CountMetric createCountMetric(string name, int64_t what, optional<int64_t> condition, - vector<int64_t> states) { - CountMetric metric; - metric.set_id(StringToId(name)); - metric.set_what(what); - metric.set_bucket(TEN_MINUTES); - if (condition) { - metric.set_condition(condition.value()); - } - for (const int64_t state : states) { - metric.add_slice_by_state(state); - } - return metric; -} - -GaugeMetric createGaugeMetric(string name, int64_t what, GaugeMetric::SamplingType samplingType, - optional<int64_t> condition, optional<int64_t> triggerEvent) { - GaugeMetric metric; - metric.set_id(StringToId(name)); - metric.set_what(what); - metric.set_bucket(TEN_MINUTES); - metric.set_sampling_type(samplingType); - if (condition) { - metric.set_condition(condition.value()); - } - if (triggerEvent) { - metric.set_trigger_event(triggerEvent.value()); - } - metric.mutable_gauge_fields_filter()->set_include_all(true); - return metric; -} - -DurationMetric createDurationMetric(string name, int64_t what, optional<int64_t> condition, - vector<int64_t> states) { - DurationMetric metric; - metric.set_id(StringToId(name)); - metric.set_what(what); - metric.set_bucket(TEN_MINUTES); - if (condition) { - metric.set_condition(condition.value()); - } - for (const int64_t state : states) { - metric.add_slice_by_state(state); - } - return metric; -} - -ValueMetric createValueMetric(string name, const AtomMatcher& what, optional<int64_t> condition, - vector<int64_t> states) { - ValueMetric metric; - metric.set_id(StringToId(name)); - metric.set_what(what.id()); - metric.set_bucket(TEN_MINUTES); - metric.mutable_value_field()->set_field(what.simple_atom_matcher().atom_id()); - metric.mutable_value_field()->add_child()->set_field(2); - if (condition) { - metric.set_condition(condition.value()); - } - for (const int64_t state : states) { - metric.add_slice_by_state(state); - } - return metric; -} - -Alert createAlert(string name, int64_t metricId, int buckets, int64_t triggerSum) { - Alert alert; - alert.set_id(StringToId(name)); - alert.set_metric_id(metricId); - alert.set_num_buckets(buckets); - alert.set_trigger_if_sum_gt(triggerSum); - return alert; -} - -Subscription createSubscription(string name, Subscription_RuleType type, int64_t ruleId) { - Subscription subscription; - subscription.set_id(StringToId(name)); - subscription.set_rule_type(type); - subscription.set_rule_id(ruleId); - subscription.mutable_broadcast_subscriber_details(); - return subscription; -} - -Alarm createAlarm(string name, int64_t offsetMillis, int64_t periodMillis) { - Alarm alarm; - alarm.set_id(StringToId(name)); - alarm.set_offset_millis(offsetMillis); - alarm.set_period_millis(periodMillis); - return alarm; -} -} // anonymous namespace - -TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) { - StatsdConfig config; - AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10); - int64_t matcherId = matcher.id(); - *config.add_atom_matcher() = matcher; - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN); - vector<bool> cycleTracker(1, false); - unordered_map<int64_t, int> newAtomMatchingTrackerMap; - newAtomMatchingTrackerMap[matcherId] = 0; - EXPECT_TRUE(determineMatcherUpdateStatus(config, 0, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); - EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE); -} - -TEST_F(ConfigUpdateTest, TestSimpleMatcherReplace) { - StatsdConfig config; - AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10); - *config.add_atom_matcher() = matcher; - - EXPECT_TRUE(initConfig(config)); - - StatsdConfig newConfig; - // Same id, different atom, so should be replaced. - AtomMatcher newMatcher = CreateSimpleAtomMatcher("TEST", /*atom=*/11); - int64_t matcherId = newMatcher.id(); - EXPECT_EQ(matcherId, matcher.id()); - *newConfig.add_atom_matcher() = newMatcher; - - vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN); - vector<bool> cycleTracker(1, false); - unordered_map<int64_t, int> newAtomMatchingTrackerMap; - newAtomMatchingTrackerMap[matcherId] = 0; - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); - EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestSimpleMatcherNew) { - StatsdConfig config; - AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10); - *config.add_atom_matcher() = matcher; - - EXPECT_TRUE(initConfig(config)); - - StatsdConfig newConfig; - // Different id, so should be a new matcher. - AtomMatcher newMatcher = CreateSimpleAtomMatcher("DIFFERENT_NAME", /*atom=*/10); - int64_t matcherId = newMatcher.id(); - EXPECT_NE(matcherId, matcher.id()); - *newConfig.add_atom_matcher() = newMatcher; - - vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN); - vector<bool> cycleTracker(1, false); - unordered_map<int64_t, int> newAtomMatchingTrackerMap; - newAtomMatchingTrackerMap[matcherId] = 0; - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); - EXPECT_EQ(matchersToUpdate[0], UPDATE_NEW); -} - -TEST_F(ConfigUpdateTest, TestCombinationMatcherPreserve) { - StatsdConfig config; - AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11); - *config.add_atom_matcher() = matcher2; - int64_t matcher2Id = matcher2.id(); - - AtomMatcher matcher3; - matcher3.set_id(StringToId("TEST3")); - AtomMatcher_Combination* combination = matcher3.mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(matcher1Id); - combination->add_matcher(matcher2Id); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - EXPECT_TRUE(initConfig(config)); - - StatsdConfig newConfig; - unordered_map<int64_t, int> newAtomMatchingTrackerMap; - // Same matchers, different order, all should be preserved. - *newConfig.add_atom_matcher() = matcher2; - newAtomMatchingTrackerMap[matcher2Id] = 0; - *newConfig.add_atom_matcher() = matcher3; - newAtomMatchingTrackerMap[matcher3Id] = 1; - *newConfig.add_atom_matcher() = matcher1; - newAtomMatchingTrackerMap[matcher1Id] = 2; - - vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN); - vector<bool> cycleTracker(3, false); - // Only update the combination. It should recurse the two child matchers and preserve all 3. - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); - EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE); - EXPECT_EQ(matchersToUpdate[1], UPDATE_PRESERVE); - EXPECT_EQ(matchersToUpdate[2], UPDATE_PRESERVE); -} - -TEST_F(ConfigUpdateTest, TestCombinationMatcherReplace) { - StatsdConfig config; - AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11); - *config.add_atom_matcher() = matcher2; - int64_t matcher2Id = matcher2.id(); - - AtomMatcher matcher3; - matcher3.set_id(StringToId("TEST3")); - AtomMatcher_Combination* combination = matcher3.mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(matcher1Id); - combination->add_matcher(matcher2Id); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - EXPECT_TRUE(initConfig(config)); - - // Change the logical operation of the combination matcher, causing a replacement. - matcher3.mutable_combination()->set_operation(LogicalOperation::AND); - - StatsdConfig newConfig; - unordered_map<int64_t, int> newAtomMatchingTrackerMap; - *newConfig.add_atom_matcher() = matcher2; - newAtomMatchingTrackerMap[matcher2Id] = 0; - *newConfig.add_atom_matcher() = matcher3; - newAtomMatchingTrackerMap[matcher3Id] = 1; - *newConfig.add_atom_matcher() = matcher1; - newAtomMatchingTrackerMap[matcher1Id] = 2; - - vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN); - vector<bool> cycleTracker(3, false); - // Only update the combination. The simple matchers should not be evaluated. - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); - EXPECT_EQ(matchersToUpdate[0], UPDATE_UNKNOWN); - EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE); - EXPECT_EQ(matchersToUpdate[2], UPDATE_UNKNOWN); -} - -TEST_F(ConfigUpdateTest, TestCombinationMatcherDepsChange) { - StatsdConfig config; - AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11); - *config.add_atom_matcher() = matcher2; - int64_t matcher2Id = matcher2.id(); - - AtomMatcher matcher3; - matcher3.set_id(StringToId("TEST3")); - AtomMatcher_Combination* combination = matcher3.mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(matcher1Id); - combination->add_matcher(matcher2Id); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - EXPECT_TRUE(initConfig(config)); - - // Change a dependency of matcher 3. - matcher2.mutable_simple_atom_matcher()->set_atom_id(12); - - StatsdConfig newConfig; - unordered_map<int64_t, int> newAtomMatchingTrackerMap; - *newConfig.add_atom_matcher() = matcher2; - newAtomMatchingTrackerMap[matcher2Id] = 0; - *newConfig.add_atom_matcher() = matcher3; - newAtomMatchingTrackerMap[matcher3Id] = 1; - *newConfig.add_atom_matcher() = matcher1; - newAtomMatchingTrackerMap[matcher1Id] = 2; - - vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN); - vector<bool> cycleTracker(3, false); - // Only update the combination. - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); - // Matcher 2 and matcher3 must be reevaluated. Matcher 1 might, but does not need to be. - EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE); - EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestUpdateMatchers) { - StatsdConfig config; - // Will be preserved. - AtomMatcher simple1 = CreateSimpleAtomMatcher("SIMPLE1", /*atom=*/10); - int64_t simple1Id = simple1.id(); - *config.add_atom_matcher() = simple1; - - // Will be replaced. - AtomMatcher simple2 = CreateSimpleAtomMatcher("SIMPLE2", /*atom=*/11); - *config.add_atom_matcher() = simple2; - int64_t simple2Id = simple2.id(); - - // Will be removed. - AtomMatcher simple3 = CreateSimpleAtomMatcher("SIMPLE3", /*atom=*/12); - *config.add_atom_matcher() = simple3; - int64_t simple3Id = simple3.id(); - - // Will be preserved. - AtomMatcher combination1; - combination1.set_id(StringToId("combination1")); - AtomMatcher_Combination* combination = combination1.mutable_combination(); - combination->set_operation(LogicalOperation::NOT); - combination->add_matcher(simple1Id); - int64_t combination1Id = combination1.id(); - *config.add_atom_matcher() = combination1; - - // Will be replaced since it depends on simple2. - AtomMatcher combination2; - combination2.set_id(StringToId("combination2")); - combination = combination2.mutable_combination(); - combination->set_operation(LogicalOperation::AND); - combination->add_matcher(simple1Id); - combination->add_matcher(simple2Id); - int64_t combination2Id = combination2.id(); - *config.add_atom_matcher() = combination2; - - EXPECT_TRUE(initConfig(config)); - - // Change simple2, causing simple2 and combination2 to be replaced. - simple2.mutable_simple_atom_matcher()->set_atom_id(111); - - // 2 new matchers: simple4 and combination3: - AtomMatcher simple4 = CreateSimpleAtomMatcher("SIMPLE4", /*atom=*/13); - int64_t simple4Id = simple4.id(); - - AtomMatcher combination3; - combination3.set_id(StringToId("combination3")); - combination = combination3.mutable_combination(); - combination->set_operation(LogicalOperation::AND); - combination->add_matcher(simple4Id); - combination->add_matcher(simple2Id); - int64_t combination3Id = combination3.id(); - - StatsdConfig newConfig; - *newConfig.add_atom_matcher() = combination3; - *newConfig.add_atom_matcher() = simple2; - *newConfig.add_atom_matcher() = combination2; - *newConfig.add_atom_matcher() = simple1; - *newConfig.add_atom_matcher() = simple4; - *newConfig.add_atom_matcher() = combination1; - - set<int> newTagIds; - unordered_map<int64_t, int> newAtomMatchingTrackerMap; - vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; - set<int64_t> replacedMatchers; - EXPECT_TRUE(updateAtomMatchingTrackers( - newConfig, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newTagIds, - newAtomMatchingTrackerMap, newAtomMatchingTrackers, replacedMatchers)); - - ASSERT_EQ(newTagIds.size(), 3); - EXPECT_EQ(newTagIds.count(10), 1); - EXPECT_EQ(newTagIds.count(111), 1); - EXPECT_EQ(newTagIds.count(13), 1); - - ASSERT_EQ(newAtomMatchingTrackerMap.size(), 6); - EXPECT_EQ(newAtomMatchingTrackerMap.at(combination3Id), 0); - EXPECT_EQ(newAtomMatchingTrackerMap.at(simple2Id), 1); - EXPECT_EQ(newAtomMatchingTrackerMap.at(combination2Id), 2); - EXPECT_EQ(newAtomMatchingTrackerMap.at(simple1Id), 3); - EXPECT_EQ(newAtomMatchingTrackerMap.at(simple4Id), 4); - EXPECT_EQ(newAtomMatchingTrackerMap.at(combination1Id), 5); - - ASSERT_EQ(newAtomMatchingTrackers.size(), 6); - // Make sure all atom matchers are initialized: - for (const sp<AtomMatchingTracker>& tracker : newAtomMatchingTrackers) { - EXPECT_TRUE(tracker->mInitialized); - } - // Make sure preserved atom matchers are the same. - EXPECT_EQ(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(simple1Id)], - newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(simple1Id)]); - EXPECT_EQ(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination1Id)], - newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination1Id)]); - // Make sure replaced matchers are different. - EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(simple2Id)], - newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(simple2Id)]); - EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination2Id)], - newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination2Id)]); - - // Validation, make sure the matchers have the proper ids/indices. Could do more checks here. - EXPECT_EQ(newAtomMatchingTrackers[0]->getId(), combination3Id); - EXPECT_EQ(newAtomMatchingTrackers[0]->mIndex, 0); - EXPECT_EQ(newAtomMatchingTrackers[1]->getId(), simple2Id); - EXPECT_EQ(newAtomMatchingTrackers[1]->mIndex, 1); - EXPECT_EQ(newAtomMatchingTrackers[2]->getId(), combination2Id); - EXPECT_EQ(newAtomMatchingTrackers[2]->mIndex, 2); - EXPECT_EQ(newAtomMatchingTrackers[3]->getId(), simple1Id); - EXPECT_EQ(newAtomMatchingTrackers[3]->mIndex, 3); - EXPECT_EQ(newAtomMatchingTrackers[4]->getId(), simple4Id); - EXPECT_EQ(newAtomMatchingTrackers[4]->mIndex, 4); - EXPECT_EQ(newAtomMatchingTrackers[5]->getId(), combination1Id); - EXPECT_EQ(newAtomMatchingTrackers[5]->mIndex, 5); - - // Verify child indices of Combination Matchers are correct. - CombinationAtomMatchingTracker* combinationTracker1 = - static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[5].get()); - vector<int>* childMatchers = &combinationTracker1->mChildren; - EXPECT_EQ(childMatchers->size(), 1); - EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end()); - - CombinationAtomMatchingTracker* combinationTracker2 = - static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[2].get()); - childMatchers = &combinationTracker2->mChildren; - EXPECT_EQ(childMatchers->size(), 2); - EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end()); - EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end()); - - CombinationAtomMatchingTracker* combinationTracker3 = - static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[0].get()); - childMatchers = &combinationTracker3->mChildren; - EXPECT_EQ(childMatchers->size(), 2); - EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end()); - EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 4), childMatchers->end()); - - // Expect replacedMatchers to have simple2 and combination2 - ASSERT_EQ(replacedMatchers.size(), 2); - EXPECT_NE(replacedMatchers.find(simple2Id), replacedMatchers.end()); - EXPECT_NE(replacedMatchers.find(combination2Id), replacedMatchers.end()); -} - -TEST_F(ConfigUpdateTest, TestSimpleConditionPreserve) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - set<int64_t> replacedMatchers; - vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN); - vector<bool> cycleTracker(1, false); - unordered_map<int64_t, int> newConditionTrackerMap; - newConditionTrackerMap[predicate.id()] = 0; - EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); - EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE); -} - -TEST_F(ConfigUpdateTest, TestSimpleConditionReplace) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - EXPECT_TRUE(initConfig(config)); - - // Modify the predicate. - config.mutable_predicate(0)->mutable_simple_predicate()->set_count_nesting(true); - - set<int64_t> replacedMatchers; - vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN); - vector<bool> cycleTracker(1, false); - unordered_map<int64_t, int> newConditionTrackerMap; - newConditionTrackerMap[predicate.id()] = 0; - EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); - EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestSimpleConditionDepsChange) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - int64_t startMatcherId = startMatcher.id(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - EXPECT_TRUE(initConfig(config)); - - // Start matcher was replaced. - set<int64_t> replacedMatchers; - replacedMatchers.insert(startMatcherId); - - vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN); - vector<bool> cycleTracker(1, false); - unordered_map<int64_t, int> newConditionTrackerMap; - newConditionTrackerMap[predicate.id()] = 0; - EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); - EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestCombinationConditionPreserve) { - StatsdConfig config; - AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = screenOnMatcher; - AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = screenOffMatcher; - - Predicate simple1 = CreateScreenIsOnPredicate(); - *config.add_predicate() = simple1; - Predicate simple2 = CreateScreenIsOffPredicate(); - *config.add_predicate() = simple2; - - Predicate combination1; - combination1.set_id(StringToId("COMBINATION1")); - Predicate_Combination* combinationInternal = combination1.mutable_combination(); - combinationInternal->set_operation(LogicalOperation::NAND); - combinationInternal->add_predicate(simple1.id()); - combinationInternal->add_predicate(simple2.id()); - *config.add_predicate() = combination1; - - EXPECT_TRUE(initConfig(config)); - - // Same predicates, different order - StatsdConfig newConfig; - unordered_map<int64_t, int> newConditionTrackerMap; - *newConfig.add_predicate() = combination1; - newConditionTrackerMap[combination1.id()] = 0; - *newConfig.add_predicate() = simple2; - newConditionTrackerMap[simple2.id()] = 1; - *newConfig.add_predicate() = simple1; - newConditionTrackerMap[simple1.id()] = 2; - - set<int64_t> replacedMatchers; - vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN); - vector<bool> cycleTracker(3, false); - // Only update the combination. It should recurse the two child predicates and preserve all 3. - EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); - EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE); - EXPECT_EQ(conditionsToUpdate[1], UPDATE_PRESERVE); - EXPECT_EQ(conditionsToUpdate[2], UPDATE_PRESERVE); -} - -TEST_F(ConfigUpdateTest, TestCombinationConditionReplace) { - StatsdConfig config; - AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = screenOnMatcher; - AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = screenOffMatcher; - - Predicate simple1 = CreateScreenIsOnPredicate(); - *config.add_predicate() = simple1; - Predicate simple2 = CreateScreenIsOffPredicate(); - *config.add_predicate() = simple2; - - Predicate combination1; - combination1.set_id(StringToId("COMBINATION1")); - Predicate_Combination* combinationInternal = combination1.mutable_combination(); - combinationInternal->set_operation(LogicalOperation::NAND); - combinationInternal->add_predicate(simple1.id()); - combinationInternal->add_predicate(simple2.id()); - *config.add_predicate() = combination1; - - EXPECT_TRUE(initConfig(config)); - - // Changing the logical operation changes the predicate definition, so it should be replaced. - combination1.mutable_combination()->set_operation(LogicalOperation::OR); - - StatsdConfig newConfig; - unordered_map<int64_t, int> newConditionTrackerMap; - *newConfig.add_predicate() = combination1; - newConditionTrackerMap[combination1.id()] = 0; - *newConfig.add_predicate() = simple2; - newConditionTrackerMap[simple2.id()] = 1; - *newConfig.add_predicate() = simple1; - newConditionTrackerMap[simple1.id()] = 2; - - set<int64_t> replacedMatchers; - vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN); - vector<bool> cycleTracker(3, false); - // Only update the combination. The simple conditions should not be evaluated. - EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); - EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); - EXPECT_EQ(conditionsToUpdate[1], UPDATE_UNKNOWN); - EXPECT_EQ(conditionsToUpdate[2], UPDATE_UNKNOWN); -} - -TEST_F(ConfigUpdateTest, TestCombinationConditionDepsChange) { - StatsdConfig config; - AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = screenOnMatcher; - AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = screenOffMatcher; - - Predicate simple1 = CreateScreenIsOnPredicate(); - *config.add_predicate() = simple1; - Predicate simple2 = CreateScreenIsOffPredicate(); - *config.add_predicate() = simple2; - - Predicate combination1; - combination1.set_id(StringToId("COMBINATION1")); - Predicate_Combination* combinationInternal = combination1.mutable_combination(); - combinationInternal->set_operation(LogicalOperation::NAND); - combinationInternal->add_predicate(simple1.id()); - combinationInternal->add_predicate(simple2.id()); - *config.add_predicate() = combination1; - - EXPECT_TRUE(initConfig(config)); - - simple2.mutable_simple_predicate()->set_count_nesting(false); - - StatsdConfig newConfig; - unordered_map<int64_t, int> newConditionTrackerMap; - *newConfig.add_predicate() = combination1; - newConditionTrackerMap[combination1.id()] = 0; - *newConfig.add_predicate() = simple2; - newConditionTrackerMap[simple2.id()] = 1; - *newConfig.add_predicate() = simple1; - newConditionTrackerMap[simple1.id()] = 2; - - set<int64_t> replacedMatchers; - vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN); - vector<bool> cycleTracker(3, false); - // Only update the combination. Simple2 and combination1 must be evaluated. - EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); - EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); - EXPECT_EQ(conditionsToUpdate[1], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestUpdateConditions) { - StatsdConfig config; - // Add atom matchers. These are mostly needed for initStatsdConfig - AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); - int64_t matcher2Id = matcher2.id(); - *config.add_atom_matcher() = matcher2; - - AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher(); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher(); - int64_t matcher4Id = matcher4.id(); - *config.add_atom_matcher() = matcher4; - - AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher(); - int64_t matcher5Id = matcher5.id(); - *config.add_atom_matcher() = matcher5; - - AtomMatcher matcher6 = CreateBatterySaverModeStopAtomMatcher(); - int64_t matcher6Id = matcher6.id(); - *config.add_atom_matcher() = matcher6; - - // Add the predicates. - // Will be preserved. - Predicate simple1 = CreateScreenIsOnPredicate(); - int64_t simple1Id = simple1.id(); - *config.add_predicate() = simple1; - - // Will be preserved. - Predicate simple2 = CreateScheduledJobPredicate(); - int64_t simple2Id = simple2.id(); - *config.add_predicate() = simple2; - - // Will be replaced. - Predicate simple3 = CreateBatterySaverModePredicate(); - int64_t simple3Id = simple3.id(); - *config.add_predicate() = simple3; - - // Will be preserved - Predicate combination1; - combination1.set_id(StringToId("COMBINATION1")); - combination1.mutable_combination()->set_operation(LogicalOperation::AND); - combination1.mutable_combination()->add_predicate(simple1Id); - combination1.mutable_combination()->add_predicate(simple2Id); - int64_t combination1Id = combination1.id(); - *config.add_predicate() = combination1; - - // Will be replaced since simple3 will be replaced. - Predicate combination2; - combination2.set_id(StringToId("COMBINATION2")); - combination2.mutable_combination()->set_operation(LogicalOperation::OR); - combination2.mutable_combination()->add_predicate(simple1Id); - combination2.mutable_combination()->add_predicate(simple3Id); - int64_t combination2Id = combination2.id(); - *config.add_predicate() = combination2; - - // Will be removed. - Predicate combination3; - combination3.set_id(StringToId("COMBINATION3")); - combination3.mutable_combination()->set_operation(LogicalOperation::NOT); - combination3.mutable_combination()->add_predicate(simple2Id); - int64_t combination3Id = combination3.id(); - *config.add_predicate() = combination3; - - EXPECT_TRUE(initConfig(config)); - - // Mark marcher 5 as replaced. Causes simple3, and therefore combination2 to be replaced. - set<int64_t> replacedMatchers; - replacedMatchers.insert(matcher6Id); - - // Change the condition of simple1 to false. - ASSERT_EQ(oldConditionTrackers[0]->getConditionId(), simple1Id); - LogEvent event(/*uid=*/0, /*pid=*/0); // Empty event is fine since there are no dimensions. - // Mark the stop matcher as matched, condition should be false. - vector<MatchingState> eventMatcherValues(6, MatchingState::kNotMatched); - eventMatcherValues[1] = MatchingState::kMatched; - vector<ConditionState> tmpConditionCache(6, ConditionState::kNotEvaluated); - vector<bool> conditionChangeCache(6, false); - oldConditionTrackers[0]->evaluateCondition(event, eventMatcherValues, oldConditionTrackers, - tmpConditionCache, conditionChangeCache); - EXPECT_EQ(tmpConditionCache[0], ConditionState::kFalse); - EXPECT_EQ(conditionChangeCache[0], true); - - // New combination predicate. Should have an initial condition of true since it is NOT(simple1). - Predicate combination4; - combination4.set_id(StringToId("COMBINATION4")); - combination4.mutable_combination()->set_operation(LogicalOperation::NOT); - combination4.mutable_combination()->add_predicate(simple1Id); - int64_t combination4Id = combination4.id(); - *config.add_predicate() = combination4; - - // Map the matchers in reverse order to force the indices to change. - std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; - const int matcher6Index = 0; - newAtomMatchingTrackerMap[matcher6Id] = 0; - const int matcher5Index = 1; - newAtomMatchingTrackerMap[matcher5Id] = 1; - const int matcher4Index = 2; - newAtomMatchingTrackerMap[matcher4Id] = 2; - const int matcher3Index = 3; - newAtomMatchingTrackerMap[matcher3Id] = 3; - const int matcher2Index = 4; - newAtomMatchingTrackerMap[matcher2Id] = 4; - const int matcher1Index = 5; - newAtomMatchingTrackerMap[matcher1Id] = 5; - - StatsdConfig newConfig; - *newConfig.add_predicate() = simple3; - const int simple3Index = 0; - *newConfig.add_predicate() = combination2; - const int combination2Index = 1; - *newConfig.add_predicate() = combination4; - const int combination4Index = 2; - *newConfig.add_predicate() = simple2; - const int simple2Index = 3; - *newConfig.add_predicate() = combination1; - const int combination1Index = 4; - *newConfig.add_predicate() = simple1; - const int simple1Index = 5; - - unordered_map<int64_t, int> newConditionTrackerMap; - vector<sp<ConditionTracker>> newConditionTrackers; - unordered_map<int, vector<int>> trackerToConditionMap; - std::vector<ConditionState> conditionCache; - std::set<int64_t> replacedConditions; - EXPECT_TRUE(updateConditions(key, newConfig, newAtomMatchingTrackerMap, replacedMatchers, - oldConditionTrackerMap, oldConditionTrackers, - newConditionTrackerMap, newConditionTrackers, - trackerToConditionMap, conditionCache, replacedConditions)); - - unordered_map<int64_t, int> expectedConditionTrackerMap = { - {simple1Id, simple1Index}, {simple2Id, simple2Index}, - {simple3Id, simple3Index}, {combination1Id, combination1Index}, - {combination2Id, combination2Index}, {combination4Id, combination4Index}, - }; - EXPECT_THAT(newConditionTrackerMap, ContainerEq(expectedConditionTrackerMap)); - - ASSERT_EQ(newConditionTrackers.size(), 6); - // Make sure all conditions are initialized: - for (const sp<ConditionTracker>& tracker : newConditionTrackers) { - EXPECT_TRUE(tracker->mInitialized); - } - - // Make sure preserved conditions are the same. - EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple1Id)], - newConditionTrackers[newConditionTrackerMap.at(simple1Id)]); - EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple2Id)], - newConditionTrackers[newConditionTrackerMap.at(simple2Id)]); - EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(combination1Id)], - newConditionTrackers[newConditionTrackerMap.at(combination1Id)]); - - // Make sure replaced conditions are different and included in replacedConditions. - EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(simple3Id)], - newConditionTrackers[newConditionTrackerMap.at(simple3Id)]); - EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(combination2Id)], - newConditionTrackers[newConditionTrackerMap.at(combination2Id)]); - EXPECT_THAT(replacedConditions, ContainerEq(set({simple3Id, combination2Id}))); - - // Verify the trackerToConditionMap - ASSERT_EQ(trackerToConditionMap.size(), 6); - const vector<int>& matcher1Conditions = trackerToConditionMap[matcher1Index]; - EXPECT_THAT(matcher1Conditions, UnorderedElementsAre(simple1Index, combination1Index, - combination2Index, combination4Index)); - const vector<int>& matcher2Conditions = trackerToConditionMap[matcher2Index]; - EXPECT_THAT(matcher2Conditions, UnorderedElementsAre(simple1Index, combination1Index, - combination2Index, combination4Index)); - const vector<int>& matcher3Conditions = trackerToConditionMap[matcher3Index]; - EXPECT_THAT(matcher3Conditions, UnorderedElementsAre(simple2Index, combination1Index)); - const vector<int>& matcher4Conditions = trackerToConditionMap[matcher4Index]; - EXPECT_THAT(matcher4Conditions, UnorderedElementsAre(simple2Index, combination1Index)); - const vector<int>& matcher5Conditions = trackerToConditionMap[matcher5Index]; - EXPECT_THAT(matcher5Conditions, UnorderedElementsAre(simple3Index, combination2Index)); - const vector<int>& matcher6Conditions = trackerToConditionMap[matcher6Index]; - EXPECT_THAT(matcher6Conditions, UnorderedElementsAre(simple3Index, combination2Index)); - - // Verify the conditionCache. Specifically, simple1 is false and combination4 is true. - ASSERT_EQ(conditionCache.size(), 6); - EXPECT_EQ(conditionCache[simple1Index], ConditionState::kFalse); - EXPECT_EQ(conditionCache[simple2Index], ConditionState::kUnknown); - EXPECT_EQ(conditionCache[simple3Index], ConditionState::kUnknown); - EXPECT_EQ(conditionCache[combination1Index], ConditionState::kUnknown); - EXPECT_EQ(conditionCache[combination2Index], ConditionState::kUnknown); - EXPECT_EQ(conditionCache[combination4Index], ConditionState::kTrue); - - // Verify tracker indices/ids are correct. - EXPECT_EQ(newConditionTrackers[simple1Index]->getConditionId(), simple1Id); - EXPECT_EQ(newConditionTrackers[simple1Index]->mIndex, simple1Index); - EXPECT_TRUE(newConditionTrackers[simple1Index]->IsSimpleCondition()); - EXPECT_EQ(newConditionTrackers[simple2Index]->getConditionId(), simple2Id); - EXPECT_EQ(newConditionTrackers[simple2Index]->mIndex, simple2Index); - EXPECT_TRUE(newConditionTrackers[simple2Index]->IsSimpleCondition()); - EXPECT_EQ(newConditionTrackers[simple3Index]->getConditionId(), simple3Id); - EXPECT_EQ(newConditionTrackers[simple3Index]->mIndex, simple3Index); - EXPECT_TRUE(newConditionTrackers[simple3Index]->IsSimpleCondition()); - EXPECT_EQ(newConditionTrackers[combination1Index]->getConditionId(), combination1Id); - EXPECT_EQ(newConditionTrackers[combination1Index]->mIndex, combination1Index); - EXPECT_FALSE(newConditionTrackers[combination1Index]->IsSimpleCondition()); - EXPECT_EQ(newConditionTrackers[combination2Index]->getConditionId(), combination2Id); - EXPECT_EQ(newConditionTrackers[combination2Index]->mIndex, combination2Index); - EXPECT_FALSE(newConditionTrackers[combination2Index]->IsSimpleCondition()); - EXPECT_EQ(newConditionTrackers[combination4Index]->getConditionId(), combination4Id); - EXPECT_EQ(newConditionTrackers[combination4Index]->mIndex, combination4Index); - EXPECT_FALSE(newConditionTrackers[combination4Index]->IsSimpleCondition()); - - // Verify preserved trackers have indices updated. - SimpleConditionTracker* simpleTracker1 = - static_cast<SimpleConditionTracker*>(newConditionTrackers[simple1Index].get()); - EXPECT_EQ(simpleTracker1->mStartLogMatcherIndex, matcher1Index); - EXPECT_EQ(simpleTracker1->mStopLogMatcherIndex, matcher2Index); - EXPECT_EQ(simpleTracker1->mStopAllLogMatcherIndex, -1); - - SimpleConditionTracker* simpleTracker2 = - static_cast<SimpleConditionTracker*>(newConditionTrackers[simple2Index].get()); - EXPECT_EQ(simpleTracker2->mStartLogMatcherIndex, matcher3Index); - EXPECT_EQ(simpleTracker2->mStopLogMatcherIndex, matcher4Index); - EXPECT_EQ(simpleTracker2->mStopAllLogMatcherIndex, -1); - - CombinationConditionTracker* combinationTracker1 = static_cast<CombinationConditionTracker*>( - newConditionTrackers[combination1Index].get()); - EXPECT_THAT(combinationTracker1->mChildren, UnorderedElementsAre(simple1Index, simple2Index)); - EXPECT_THAT(combinationTracker1->mUnSlicedChildren, - UnorderedElementsAre(simple1Index, simple2Index)); - EXPECT_THAT(combinationTracker1->mSlicedChildren, IsEmpty()); -} - -TEST_F(ConfigUpdateTest, TestUpdateStates) { - StatsdConfig config; - // Add states. - // Will be replaced because we add a state map. - State state1 = CreateScreenState(); - int64_t state1Id = state1.id(); - *config.add_state() = state1; - - // Will be preserved. - State state2 = CreateUidProcessState(); - int64_t state2Id = state2.id(); - *config.add_state() = state2; - - // Will be replaced since the atom changes from overlay to screen. - State state3 = CreateOverlayState(); - int64_t state3Id = state3.id(); - *config.add_state() = state3; - - EXPECT_TRUE(initConfig(config)); - - // Change definitions of state1 and state3. - int64_t screenOnId = 0x4321, screenOffId = 0x1234; - *state1.mutable_map() = CreateScreenStateSimpleOnOffMap(screenOnId, screenOffId); - state3.set_atom_id(util::SCREEN_STATE_CHANGED); - - StatsdConfig newConfig; - *newConfig.add_state() = state3; - *newConfig.add_state() = state1; - *newConfig.add_state() = state2; - - unordered_map<int64_t, int> stateAtomIdMap; - unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; - map<int64_t, uint64_t> newStateProtoHashes; - set<int64_t> replacedStates; - EXPECT_TRUE(updateStates(newConfig, oldStateHashes, stateAtomIdMap, allStateGroupMaps, - newStateProtoHashes, replacedStates)); - EXPECT_THAT(replacedStates, ContainerEq(set({state1Id, state3Id}))); - - unordered_map<int64_t, int> expectedStateAtomIdMap = { - {state1Id, util::SCREEN_STATE_CHANGED}, - {state2Id, util::UID_PROCESS_STATE_CHANGED}, - {state3Id, util::SCREEN_STATE_CHANGED}}; - EXPECT_THAT(stateAtomIdMap, ContainerEq(expectedStateAtomIdMap)); - - unordered_map<int64_t, unordered_map<int, int64_t>> expectedStateGroupMaps = { - {state1Id, - {{android::view::DisplayStateEnum::DISPLAY_STATE_OFF, screenOffId}, - {android::view::DisplayStateEnum::DISPLAY_STATE_ON, screenOnId}}}}; - EXPECT_THAT(allStateGroupMaps, ContainerEq(expectedStateGroupMaps)); -} - -TEST_F(ConfigUpdateTest, TestEventMetricPreserve) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - EventMetric* metric = config.add_event_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->set_condition(predicate.id()); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); -} - -TEST_F(ConfigUpdateTest, TestEventMetricActivationAdded) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - EventMetric* metric = config.add_event_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->set_condition(predicate.id()); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - // Add a metric activation, which should change the proto, causing replacement. - MetricActivation* activation = config.add_metric_activation(); - activation->set_metric_id(12345); - EventActivation* eventActivation = activation->add_event_activation(); - eventActivation->set_atom_matcher_id(startMatcher.id()); - eventActivation->set_ttl_seconds(5); - - unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}}; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestEventMetricWhatChanged) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - EventMetric* metric = config.add_event_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->set_condition(predicate.id()); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestEventMetricConditionChanged) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - EventMetric* metric = config.add_event_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->set_condition(predicate.id()); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestMetricConditionLinkDepsChanged) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - Predicate linkPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = linkPredicate; - - EventMetric* metric = config.add_event_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->set_condition(predicate.id()); - // Doesn't make sense as a real metric definition, but suffices as a separate predicate - // From the one in the condition. - MetricConditionLink* link = metric->add_links(); - link->set_condition(linkPredicate.id()); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{linkPredicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - EventMetric* metric = config.add_event_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->set_condition(predicate.id()); - - MetricActivation* activation = config.add_metric_activation(); - activation->set_metric_id(12345); - EventActivation* eventActivation = activation->add_event_activation(); - eventActivation->set_atom_matcher_id(startMatcher.id()); - eventActivation->set_ttl_seconds(5); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}}; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {startMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestCountMetricPreserve) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - State sliceState = CreateScreenState(); - *config.add_state() = sliceState; - - CountMetric* metric = config.add_count_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->set_condition(predicate.id()); - metric->add_slice_by_state(sliceState.id()); - metric->set_bucket(ONE_HOUR); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); -} - -TEST_F(ConfigUpdateTest, TestCountMetricDefinitionChange) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - CountMetric* metric = config.add_count_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->set_condition(predicate.id()); - metric->set_bucket(ONE_HOUR); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - // Change bucket size, which should change the proto, causing replacement. - metric->set_bucket(TEN_MINUTES); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestCountMetricWhatChanged) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - CountMetric* metric = config.add_count_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->set_condition(predicate.id()); - metric->set_bucket(ONE_HOUR); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestCountMetricConditionChanged) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - CountMetric* metric = config.add_count_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->set_condition(predicate.id()); - metric->set_bucket(ONE_HOUR); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestCountMetricStateChanged) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - State sliceState = CreateScreenState(); - *config.add_state() = sliceState; - - CountMetric* metric = config.add_count_metric(); - metric->set_id(12345); - metric->set_what(whatMatcher.id()); - metric->add_slice_by_state(sliceState.id()); - metric->set_bucket(ONE_HOUR); - - // Create an initial config. - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{sliceState.id()}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestGaugeMetricPreserve) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - *config.add_gauge_metric() = createGaugeMetric( - "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt); - - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); -} - -TEST_F(ConfigUpdateTest, TestGaugeMetricDefinitionChange) { - StatsdConfig config; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - *config.add_gauge_metric() = createGaugeMetric( - "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt); - - EXPECT_TRUE(initConfig(config)); - - // Change split bucket on app upgrade, which should change the proto, causing replacement. - config.mutable_gauge_metric(0)->set_split_bucket_for_app_upgrade(false); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestGaugeMetricWhatChanged) { - StatsdConfig config; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - *config.add_gauge_metric() = createGaugeMetric( - "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt); - - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestGaugeMetricConditionChanged) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - *config.add_gauge_metric() = createGaugeMetric( - "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt); - - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestGaugeMetricTriggerEventChanged) { - StatsdConfig config; - AtomMatcher triggerEvent = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = triggerEvent; - AtomMatcher whatMatcher = CreateTemperatureAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - *config.add_gauge_metric() = createGaugeMetric( - "GAUGE1", whatMatcher.id(), GaugeMetric::FIRST_N_SAMPLES, nullopt, triggerEvent.id()); - - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {triggerEvent.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestDurationMetricPreserve) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - - Predicate what = CreateScreenIsOnPredicate(); - *config.add_predicate() = what; - Predicate condition = CreateScreenIsOffPredicate(); - *config.add_predicate() = condition; - - State sliceState = CreateScreenState(); - *config.add_state() = sliceState; - - *config.add_duration_metric() = - createDurationMetric("DURATION1", what.id(), condition.id(), {sliceState.id()}); - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); -} - -TEST_F(ConfigUpdateTest, TestDurationMetricDefinitionChange) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - - Predicate what = CreateScreenIsOnPredicate(); - *config.add_predicate() = what; - - *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {}); - EXPECT_TRUE(initConfig(config)); - - config.mutable_duration_metric(0)->set_aggregation_type(DurationMetric::MAX_SPARSE); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, /*replacedMatchers*/ {}, - /*replacedConditions=*/{}, /*replacedStates=*/{}, - metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestDurationMetricWhatChanged) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - - Predicate what = CreateScreenIsOnPredicate(); - *config.add_predicate() = what; - - *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {}); - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{what.id()}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestDurationMetricConditionChanged) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - - Predicate what = CreateScreenIsOnPredicate(); - *config.add_predicate() = what; - Predicate condition = CreateScreenIsOffPredicate(); - *config.add_predicate() = condition; - - *config.add_duration_metric() = createDurationMetric("DURATION", what.id(), condition.id(), {}); - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{condition.id()}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestDurationMetricStateChange) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - - Predicate what = CreateScreenIsOnPredicate(); - *config.add_predicate() = what; - - State sliceState = CreateScreenState(); - *config.add_state() = sliceState; - - *config.add_duration_metric() = - createDurationMetric("DURATION1", what.id(), nullopt, {sliceState.id()}); - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{sliceState.id()}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestValueMetricPreserve) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateTemperatureAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - State sliceState = CreateScreenState(); - *config.add_state() = sliceState; - - *config.add_value_metric() = - createValueMetric("VALUE1", whatMatcher, predicate.id(), {sliceState.id()}); - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); -} - -TEST_F(ConfigUpdateTest, TestValueMetricDefinitionChange) { - StatsdConfig config; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {}); - EXPECT_TRUE(initConfig(config)); - - // Change skip zero diff output, which should change the proto, causing replacement. - config.mutable_value_metric(0)->set_skip_zero_diff_output(true); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestValueMetricWhatChanged) { - StatsdConfig config; - AtomMatcher whatMatcher = CreateTemperatureAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {}); - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestValueMetricConditionChanged) { - StatsdConfig config; - AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = startMatcher; - AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = stopMatcher; - AtomMatcher whatMatcher = CreateTemperatureAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - Predicate predicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = predicate; - - *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, predicate.id(), {}); - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestValueMetricStateChanged) { - StatsdConfig config; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - State sliceState = CreateScreenState(); - *config.add_state() = sliceState; - - *config.add_value_metric() = - createValueMetric("VALUE1", whatMatcher, nullopt, {sliceState.id()}); - EXPECT_TRUE(initConfig(config)); - - unordered_map<int64_t, int> metricToActivationMap; - vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{sliceState.id()}, metricsToUpdate)); - EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) { - StatsdConfig config; - - // Add atom matchers/predicates. These are mostly needed for initStatsdConfig - AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); - int64_t matcher2Id = matcher2.id(); - *config.add_atom_matcher() = matcher2; - - AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher(); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher(); - int64_t matcher4Id = matcher4.id(); - *config.add_atom_matcher() = matcher4; - - AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher(); - int64_t matcher5Id = matcher5.id(); - *config.add_atom_matcher() = matcher5; - - Predicate predicate1 = CreateScreenIsOnPredicate(); - int64_t predicate1Id = predicate1.id(); - *config.add_predicate() = predicate1; - - Predicate predicate2 = CreateScheduledJobPredicate(); - int64_t predicate2Id = predicate2.id(); - *config.add_predicate() = predicate2; - - // Add a few event metrics. - // Will be preserved. - EventMetric event1 = createEventMetric("EVENT1", matcher1Id, predicate2Id); - int64_t event1Id = event1.id(); - *config.add_event_metric() = event1; - - // Will be replaced. - EventMetric event2 = createEventMetric("EVENT2", matcher2Id, nullopt); - int64_t event2Id = event2.id(); - *config.add_event_metric() = event2; - - // Will be replaced. - EventMetric event3 = createEventMetric("EVENT3", matcher3Id, nullopt); - int64_t event3Id = event3.id(); - *config.add_event_metric() = event3; - - MetricActivation event3Activation; - event3Activation.set_metric_id(event3Id); - EventActivation* eventActivation = event3Activation.add_event_activation(); - eventActivation->set_atom_matcher_id(matcher5Id); - eventActivation->set_ttl_seconds(5); - *config.add_metric_activation() = event3Activation; - - // Will be replaced. - EventMetric event4 = createEventMetric("EVENT4", matcher4Id, predicate1Id); - int64_t event4Id = event4.id(); - *config.add_event_metric() = event4; - - // Will be deleted. - EventMetric event5 = createEventMetric("EVENT5", matcher5Id, nullopt); - int64_t event5Id = event5.id(); - *config.add_event_metric() = event5; - - EXPECT_TRUE(initConfig(config)); - - // Used later to ensure the condition wizard is replaced. Get it before doing the update. - sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard; - EXPECT_EQ(oldConditionWizard->getStrongCount(), oldMetricProducers.size() + 1); - - // Add a condition to event2, causing it to be replaced. - event2.set_condition(predicate1Id); - - // Mark matcher 5 as replaced. Causes event3 to be replaced. - set<int64_t> replacedMatchers; - replacedMatchers.insert(matcher5Id); - - // Mark predicate 1 as replaced. Causes event4 to be replaced. - set<int64_t> replacedConditions; - replacedConditions.insert(predicate1Id); - - // Fake that predicate 2 is true. - ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id); - oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0); - EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue); - - // New event metric. Should have an initial condition of true since it depends on predicate2. - EventMetric event6 = createEventMetric("EVENT6", matcher3Id, predicate2Id); - int64_t event6Id = event6.id(); - MetricActivation event6Activation; - event6Activation.set_metric_id(event6Id); - eventActivation = event6Activation.add_event_activation(); - eventActivation->set_atom_matcher_id(matcher5Id); - eventActivation->set_ttl_seconds(20); - - // Map the matchers and predicates in reverse order to force the indices to change. - std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; - const int matcher5Index = 0; - newAtomMatchingTrackerMap[matcher5Id] = 0; - const int matcher4Index = 1; - newAtomMatchingTrackerMap[matcher4Id] = 1; - const int matcher3Index = 2; - newAtomMatchingTrackerMap[matcher3Id] = 2; - const int matcher2Index = 3; - newAtomMatchingTrackerMap[matcher2Id] = 3; - const int matcher1Index = 4; - newAtomMatchingTrackerMap[matcher1Id] = 4; - // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. - vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5); - std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), - newAtomMatchingTrackers.begin()); - - std::unordered_map<int64_t, int> newConditionTrackerMap; - const int predicate2Index = 0; - newConditionTrackerMap[predicate2Id] = 0; - const int predicate1Index = 1; - newConditionTrackerMap[predicate1Id] = 1; - // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them. - vector<sp<ConditionTracker>> newConditionTrackers(2); - std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(), - newConditionTrackers.begin()); - // Fake that predicate2 is true. - vector<ConditionState> conditionCache = {ConditionState::kTrue, ConditionState::kUnknown}; - - StatsdConfig newConfig; - *newConfig.add_event_metric() = event6; - const int event6Index = 0; - *newConfig.add_event_metric() = event3; - const int event3Index = 1; - *newConfig.add_event_metric() = event1; - const int event1Index = 2; - *newConfig.add_event_metric() = event4; - const int event4Index = 3; - *newConfig.add_event_metric() = event2; - const int event2Index = 4; - *newConfig.add_metric_activation() = event3Activation; - *newConfig.add_metric_activation() = event6Activation; - - // Output data structures to validate. - unordered_map<int64_t, int> newMetricProducerMap; - vector<sp<MetricProducer>> newMetricProducers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - set<int64_t> noReportMetricIds; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - vector<int> metricsWithActivation; - set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); - - unordered_map<int64_t, int> expectedMetricProducerMap = { - {event1Id, event1Index}, {event2Id, event2Index}, {event3Id, event3Index}, - {event4Id, event4Index}, {event6Id, event6Index}, - }; - EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); - EXPECT_EQ(replacedMetrics, set<int64_t>({event2Id, event3Id, event4Id})); - - // Make sure preserved metrics are the same. - ASSERT_EQ(newMetricProducers.size(), 5); - EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(event1Id)], - newMetricProducers[newMetricProducerMap.at(event1Id)]); - - // Make sure replaced metrics are different. - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event2Id)], - newMetricProducers[newMetricProducerMap.at(event2Id)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event3Id)], - newMetricProducers[newMetricProducerMap.at(event3Id)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event4Id)], - newMetricProducers[newMetricProducerMap.at(event4Id)]); - - // Verify the conditionToMetricMap. - ASSERT_EQ(conditionToMetricMap.size(), 2); - const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index]; - EXPECT_THAT(condition1Metrics, UnorderedElementsAre(event2Index, event4Index)); - const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index]; - EXPECT_THAT(condition2Metrics, UnorderedElementsAre(event1Index, event6Index)); - - // Verify the trackerToMetricMap. - ASSERT_EQ(trackerToMetricMap.size(), 4); - const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; - EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(event1Index)); - const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index]; - EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(event2Index)); - const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index]; - EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(event3Index, event6Index)); - const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index]; - EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(event4Index)); - - // Verify event activation/deactivation maps. - ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 1); - EXPECT_THAT(activationAtomTrackerToMetricMap[matcher5Index], - UnorderedElementsAre(event3Index, event6Index)); - ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(metricsWithActivation.size(), 2); - EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(event3Index, event6Index)); - - // Verify tracker indices/ids/conditions are correct. - EXPECT_EQ(newMetricProducers[event1Index]->getMetricId(), event1Id); - EXPECT_EQ(newMetricProducers[event1Index]->mConditionTrackerIndex, predicate2Index); - EXPECT_EQ(newMetricProducers[event1Index]->mCondition, ConditionState::kTrue); - EXPECT_EQ(newMetricProducers[event2Index]->getMetricId(), event2Id); - EXPECT_EQ(newMetricProducers[event2Index]->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(newMetricProducers[event2Index]->mCondition, ConditionState::kUnknown); - EXPECT_EQ(newMetricProducers[event3Index]->getMetricId(), event3Id); - EXPECT_EQ(newMetricProducers[event3Index]->mConditionTrackerIndex, -1); - EXPECT_EQ(newMetricProducers[event3Index]->mCondition, ConditionState::kTrue); - EXPECT_EQ(newMetricProducers[event4Index]->getMetricId(), event4Id); - EXPECT_EQ(newMetricProducers[event4Index]->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(newMetricProducers[event4Index]->mCondition, ConditionState::kUnknown); - EXPECT_EQ(newMetricProducers[event6Index]->getMetricId(), event6Id); - EXPECT_EQ(newMetricProducers[event6Index]->mConditionTrackerIndex, predicate2Index); - EXPECT_EQ(newMetricProducers[event6Index]->mCondition, ConditionState::kTrue); - - sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard; - EXPECT_NE(newConditionWizard, oldConditionWizard); - EXPECT_EQ(newConditionWizard->getStrongCount(), newMetricProducers.size() + 1); - oldMetricProducers.clear(); - // Only reference to the old wizard should be the one in the test. - EXPECT_EQ(oldConditionWizard->getStrongCount(), 1); -} - -TEST_F(ConfigUpdateTest, TestUpdateCountMetrics) { - StatsdConfig config; - - // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig. - AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); - int64_t matcher2Id = matcher2.id(); - *config.add_atom_matcher() = matcher2; - - AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher(); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher(); - int64_t matcher4Id = matcher4.id(); - *config.add_atom_matcher() = matcher4; - - AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher(); - int64_t matcher5Id = matcher5.id(); - *config.add_atom_matcher() = matcher5; - - Predicate predicate1 = CreateScreenIsOnPredicate(); - int64_t predicate1Id = predicate1.id(); - *config.add_predicate() = predicate1; - - State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321); - int64_t state1Id = state1.id(); - *config.add_state() = state1; - - State state2 = CreateScreenState(); - int64_t state2Id = state2.id(); - *config.add_state() = state2; - - // Add a few count metrics. - // Will be preserved. - CountMetric count1 = createCountMetric("COUNT1", matcher1Id, predicate1Id, {state1Id}); - int64_t count1Id = count1.id(); - *config.add_count_metric() = count1; - - // Will be replaced. - CountMetric count2 = createCountMetric("COUNT2", matcher2Id, nullopt, {}); - int64_t count2Id = count2.id(); - *config.add_count_metric() = count2; - - // Will be replaced. - CountMetric count3 = createCountMetric("COUNT3", matcher3Id, nullopt, {}); - int64_t count3Id = count3.id(); - *config.add_count_metric() = count3; - - // Will be replaced. - CountMetric count4 = createCountMetric("COUNT4", matcher4Id, nullopt, {state2Id}); - int64_t count4Id = count4.id(); - *config.add_count_metric() = count4; - - // Will be deleted. - CountMetric count5 = createCountMetric("COUNT5", matcher5Id, nullopt, {}); - int64_t count5Id = count5.id(); - *config.add_count_metric() = count5; - - EXPECT_TRUE(initConfig(config)); - - // Change bucket size of count2, causing it to be replaced. - count2.set_bucket(ONE_HOUR); - - // Mark matcher 3 as replaced. Causes count3 to be replaced. - set<int64_t> replacedMatchers; - replacedMatchers.insert(matcher3Id); - - // Mark state 2 as replaced and change the state to be about a different atom. - // Causes count4 to be replaced. - set<int64_t> replacedStates; - replacedStates.insert(state2Id); - state2.set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED); - - // Fake that predicate 1 is true for count metric 1. - ASSERT_EQ(oldMetricProducers[0]->getMetricId(), count1Id); - oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0); - EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue); - - EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 1); - // Tell the StateManager that the screen is on. - unique_ptr<LogEvent> event = - CreateScreenStateChangedEvent(0, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - StateManager::getInstance().onLogEvent(*event); - - // New count metric. Should have an initial condition of true since it depends on predicate1. - CountMetric count6 = createCountMetric("EVENT6", matcher2Id, predicate1Id, {state1Id}); - int64_t count6Id = count6.id(); - - // Map the matchers and predicates in reverse order to force the indices to change. - std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; - const int matcher5Index = 0; - newAtomMatchingTrackerMap[matcher5Id] = 0; - const int matcher4Index = 1; - newAtomMatchingTrackerMap[matcher4Id] = 1; - const int matcher3Index = 2; - newAtomMatchingTrackerMap[matcher3Id] = 2; - const int matcher2Index = 3; - newAtomMatchingTrackerMap[matcher2Id] = 3; - const int matcher1Index = 4; - newAtomMatchingTrackerMap[matcher1Id] = 4; - // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. - vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5); - std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), - newAtomMatchingTrackers.begin()); - - std::unordered_map<int64_t, int> newConditionTrackerMap; - const int predicate1Index = 0; - newConditionTrackerMap[predicate1Id] = 0; - // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them. - vector<sp<ConditionTracker>> newConditionTrackers(1); - std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(), - newConditionTrackers.begin()); - // Fake that predicate1 is true for all new metrics. - vector<ConditionState> conditionCache = {ConditionState::kTrue}; - - StatsdConfig newConfig; - *newConfig.add_count_metric() = count6; - const int count6Index = 0; - *newConfig.add_count_metric() = count3; - const int count3Index = 1; - *newConfig.add_count_metric() = count1; - const int count1Index = 2; - *newConfig.add_count_metric() = count4; - const int count4Index = 3; - *newConfig.add_count_metric() = count2; - const int count2Index = 4; - - *newConfig.add_state() = state1; - *newConfig.add_state() = state2; - - unordered_map<int64_t, int> stateAtomIdMap; - unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; - map<int64_t, uint64_t> stateProtoHashes; - EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)); - EXPECT_EQ(stateAtomIdMap[state2Id], util::BATTERY_SAVER_MODE_STATE_CHANGED); - - // Output data structures to validate. - unordered_map<int64_t, int> newMetricProducerMap; - vector<sp<MetricProducer>> newMetricProducers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - set<int64_t> noReportMetricIds; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - vector<int> metricsWithActivation; - set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{}, - newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, - oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers, - conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); - - unordered_map<int64_t, int> expectedMetricProducerMap = { - {count1Id, count1Index}, {count2Id, count2Index}, {count3Id, count3Index}, - {count4Id, count4Index}, {count6Id, count6Index}, - }; - EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); - EXPECT_EQ(replacedMetrics, set<int64_t>({count2Id, count3Id, count4Id})); - - // Make sure preserved metrics are the same. - ASSERT_EQ(newMetricProducers.size(), 5); - EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(count1Id)], - newMetricProducers[newMetricProducerMap.at(count1Id)]); - - // Make sure replaced metrics are different. - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count2Id)], - newMetricProducers[newMetricProducerMap.at(count2Id)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count3Id)], - newMetricProducers[newMetricProducerMap.at(count3Id)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count4Id)], - newMetricProducers[newMetricProducerMap.at(count4Id)]); - - // Verify the conditionToMetricMap. - ASSERT_EQ(conditionToMetricMap.size(), 1); - const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index]; - EXPECT_THAT(condition1Metrics, UnorderedElementsAre(count1Index, count6Index)); - - // Verify the trackerToMetricMap. - ASSERT_EQ(trackerToMetricMap.size(), 4); - const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; - EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(count1Index)); - const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index]; - EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(count2Index, count6Index)); - const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index]; - EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(count3Index)); - const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index]; - EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(count4Index)); - - // Verify event activation/deactivation maps. - ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(metricsWithActivation.size(), 0); - - // Verify tracker indices/ids/conditions/states are correct. - EXPECT_EQ(newMetricProducers[count1Index]->getMetricId(), count1Id); - EXPECT_EQ(newMetricProducers[count1Index]->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(newMetricProducers[count1Index]->mCondition, ConditionState::kTrue); - EXPECT_THAT(newMetricProducers[count1Index]->getSlicedStateAtoms(), - UnorderedElementsAre(util::SCREEN_STATE_CHANGED)); - EXPECT_EQ(newMetricProducers[count2Index]->getMetricId(), count2Id); - EXPECT_EQ(newMetricProducers[count2Index]->mConditionTrackerIndex, -1); - EXPECT_EQ(newMetricProducers[count2Index]->mCondition, ConditionState::kTrue); - EXPECT_TRUE(newMetricProducers[count2Index]->getSlicedStateAtoms().empty()); - EXPECT_EQ(newMetricProducers[count3Index]->getMetricId(), count3Id); - EXPECT_EQ(newMetricProducers[count3Index]->mConditionTrackerIndex, -1); - EXPECT_EQ(newMetricProducers[count3Index]->mCondition, ConditionState::kTrue); - EXPECT_TRUE(newMetricProducers[count3Index]->getSlicedStateAtoms().empty()); - EXPECT_EQ(newMetricProducers[count4Index]->getMetricId(), count4Id); - EXPECT_EQ(newMetricProducers[count4Index]->mConditionTrackerIndex, -1); - EXPECT_EQ(newMetricProducers[count4Index]->mCondition, ConditionState::kTrue); - EXPECT_THAT(newMetricProducers[count4Index]->getSlicedStateAtoms(), - UnorderedElementsAre(util::BATTERY_SAVER_MODE_STATE_CHANGED)); - EXPECT_EQ(newMetricProducers[count6Index]->getMetricId(), count6Id); - EXPECT_EQ(newMetricProducers[count6Index]->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(newMetricProducers[count6Index]->mCondition, ConditionState::kTrue); - EXPECT_THAT(newMetricProducers[count6Index]->getSlicedStateAtoms(), - UnorderedElementsAre(util::SCREEN_STATE_CHANGED)); - - oldMetricProducers.clear(); - // Ensure that the screen state StateTracker did not get deleted and replaced. - EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 2); - FieldValue screenState; - StateManager::getInstance().getStateValue(util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY, - &screenState); - EXPECT_EQ(screenState.mValue.int_value, android::view::DisplayStateEnum::DISPLAY_STATE_ON); -} - -TEST_F(ConfigUpdateTest, TestUpdateGaugeMetrics) { - StatsdConfig config; - - // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig. - AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); - int64_t matcher2Id = matcher2.id(); - *config.add_atom_matcher() = matcher2; - - AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher(); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - AtomMatcher matcher4 = CreateTemperatureAtomMatcher(); - int64_t matcher4Id = matcher4.id(); - *config.add_atom_matcher() = matcher4; - - AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE); - int64_t matcher5Id = matcher5.id(); - *config.add_atom_matcher() = matcher5; - - Predicate predicate1 = CreateScreenIsOnPredicate(); - int64_t predicate1Id = predicate1.id(); - *config.add_predicate() = predicate1; - - // Add a few gauge metrics. - // Will be preserved. - GaugeMetric gauge1 = createGaugeMetric("GAUGE1", matcher4Id, GaugeMetric::FIRST_N_SAMPLES, - predicate1Id, matcher1Id); - int64_t gauge1Id = gauge1.id(); - *config.add_gauge_metric() = gauge1; - - // Will be replaced. - GaugeMetric gauge2 = - createGaugeMetric("GAUGE2", matcher1Id, GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt); - int64_t gauge2Id = gauge2.id(); - *config.add_gauge_metric() = gauge2; - - // Will be replaced. - GaugeMetric gauge3 = createGaugeMetric("GAUGE3", matcher5Id, GaugeMetric::FIRST_N_SAMPLES, - nullopt, matcher3Id); - int64_t gauge3Id = gauge3.id(); - *config.add_gauge_metric() = gauge3; - - // Will be replaced. - GaugeMetric gauge4 = createGaugeMetric("GAUGE4", matcher3Id, GaugeMetric::RANDOM_ONE_SAMPLE, - predicate1Id, nullopt); - int64_t gauge4Id = gauge4.id(); - *config.add_gauge_metric() = gauge4; - - // Will be deleted. - GaugeMetric gauge5 = - createGaugeMetric("GAUGE5", matcher2Id, GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, {}); - int64_t gauge5Id = gauge5.id(); - *config.add_gauge_metric() = gauge5; - - EXPECT_TRUE(initConfig(config)); - - // Used later to ensure the condition wizard is replaced. Get it before doing the update. - sp<EventMatcherWizard> oldMatcherWizard = - static_cast<GaugeMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard; - EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6); - - // Change gauge2, causing it to be replaced. - gauge2.set_max_num_gauge_atoms_per_bucket(50); - - // Mark matcher 3 as replaced. Causes gauge3 and gauge4 to be replaced. - set<int64_t> replacedMatchers = {matcher3Id}; - - // New gauge metric. - GaugeMetric gauge6 = createGaugeMetric("GAUGE6", matcher5Id, GaugeMetric::FIRST_N_SAMPLES, - predicate1Id, matcher3Id); - int64_t gauge6Id = gauge6.id(); - - // Map the matchers and predicates in reverse order to force the indices to change. - std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; - const int matcher5Index = 0; - newAtomMatchingTrackerMap[matcher5Id] = 0; - const int matcher4Index = 1; - newAtomMatchingTrackerMap[matcher4Id] = 1; - const int matcher3Index = 2; - newAtomMatchingTrackerMap[matcher3Id] = 2; - const int matcher2Index = 3; - newAtomMatchingTrackerMap[matcher2Id] = 3; - const int matcher1Index = 4; - newAtomMatchingTrackerMap[matcher1Id] = 4; - // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. - vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5); - std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), - newAtomMatchingTrackers.begin()); - - std::unordered_map<int64_t, int> newConditionTrackerMap; - const int predicate1Index = 0; - newConditionTrackerMap[predicate1Id] = 0; - // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them. - vector<sp<ConditionTracker>> newConditionTrackers(1); - std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(), - newConditionTrackers.begin()); - // Say that predicate1 is unknown since the initial condition never changed. - vector<ConditionState> conditionCache = {ConditionState::kUnknown}; - - StatsdConfig newConfig; - *newConfig.add_gauge_metric() = gauge6; - const int gauge6Index = 0; - *newConfig.add_gauge_metric() = gauge3; - const int gauge3Index = 1; - *newConfig.add_gauge_metric() = gauge1; - const int gauge1Index = 2; - *newConfig.add_gauge_metric() = gauge4; - const int gauge4Index = 3; - *newConfig.add_gauge_metric() = gauge2; - const int gauge2Index = 4; - - // Output data structures to validate. - unordered_map<int64_t, int> newMetricProducerMap; - vector<sp<MetricProducer>> newMetricProducers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - set<int64_t> noReportMetricIds; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - vector<int> metricsWithActivation; - set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{}, - newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); - - unordered_map<int64_t, int> expectedMetricProducerMap = { - {gauge1Id, gauge1Index}, {gauge2Id, gauge2Index}, {gauge3Id, gauge3Index}, - {gauge4Id, gauge4Index}, {gauge6Id, gauge6Index}, - }; - EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); - EXPECT_EQ(replacedMetrics, set<int64_t>({gauge2Id, gauge3Id, gauge4Id})); - - // Make sure preserved metrics are the same. - ASSERT_EQ(newMetricProducers.size(), 5); - EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(gauge1Id)], - newMetricProducers[newMetricProducerMap.at(gauge1Id)]); - - // Make sure replaced metrics are different. - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge2Id)], - newMetricProducers[newMetricProducerMap.at(gauge2Id)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge3Id)], - newMetricProducers[newMetricProducerMap.at(gauge3Id)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge4Id)], - newMetricProducers[newMetricProducerMap.at(gauge4Id)]); - - // Verify the conditionToMetricMap. - ASSERT_EQ(conditionToMetricMap.size(), 1); - const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index]; - EXPECT_THAT(condition1Metrics, UnorderedElementsAre(gauge1Index, gauge4Index, gauge6Index)); - - // Verify the trackerToMetricMap. - ASSERT_EQ(trackerToMetricMap.size(), 4); - const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; - EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(gauge1Index, gauge2Index)); - const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index]; - EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gauge3Index, gauge4Index, gauge6Index)); - const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index]; - EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(gauge1Index)); - const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index]; - EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(gauge3Index, gauge6Index)); - - // Verify event activation/deactivation maps. - ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(metricsWithActivation.size(), 0); - - // Verify tracker indices/ids/conditions/states are correct. - GaugeMetricProducer* gaugeProducer1 = - static_cast<GaugeMetricProducer*>(newMetricProducers[gauge1Index].get()); - EXPECT_EQ(gaugeProducer1->getMetricId(), gauge1Id); - EXPECT_EQ(gaugeProducer1->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(gaugeProducer1->mCondition, ConditionState::kUnknown); - EXPECT_EQ(gaugeProducer1->mWhatMatcherIndex, matcher4Index); - GaugeMetricProducer* gaugeProducer2 = - static_cast<GaugeMetricProducer*>(newMetricProducers[gauge2Index].get()); - EXPECT_EQ(gaugeProducer2->getMetricId(), gauge2Id); - EXPECT_EQ(gaugeProducer2->mConditionTrackerIndex, -1); - EXPECT_EQ(gaugeProducer2->mCondition, ConditionState::kTrue); - EXPECT_EQ(gaugeProducer2->mWhatMatcherIndex, matcher1Index); - GaugeMetricProducer* gaugeProducer3 = - static_cast<GaugeMetricProducer*>(newMetricProducers[gauge3Index].get()); - EXPECT_EQ(gaugeProducer3->getMetricId(), gauge3Id); - EXPECT_EQ(gaugeProducer3->mConditionTrackerIndex, -1); - EXPECT_EQ(gaugeProducer3->mCondition, ConditionState::kTrue); - EXPECT_EQ(gaugeProducer3->mWhatMatcherIndex, matcher5Index); - GaugeMetricProducer* gaugeProducer4 = - static_cast<GaugeMetricProducer*>(newMetricProducers[gauge4Index].get()); - EXPECT_EQ(gaugeProducer4->getMetricId(), gauge4Id); - EXPECT_EQ(gaugeProducer4->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(gaugeProducer4->mCondition, ConditionState::kUnknown); - EXPECT_EQ(gaugeProducer4->mWhatMatcherIndex, matcher3Index); - GaugeMetricProducer* gaugeProducer6 = - static_cast<GaugeMetricProducer*>(newMetricProducers[gauge6Index].get()); - EXPECT_EQ(gaugeProducer6->getMetricId(), gauge6Id); - EXPECT_EQ(gaugeProducer6->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(gaugeProducer6->mCondition, ConditionState::kUnknown); - EXPECT_EQ(gaugeProducer6->mWhatMatcherIndex, matcher5Index); - - sp<EventMatcherWizard> newMatcherWizard = gaugeProducer1->mEventMatcherWizard; - EXPECT_NE(newMatcherWizard, oldMatcherWizard); - EXPECT_EQ(newMatcherWizard->getStrongCount(), 6); - oldMetricProducers.clear(); - // Only reference to the old wizard should be the one in the test. - EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1); -} - -TEST_F(ConfigUpdateTest, TestUpdateDurationMetrics) { - StatsdConfig config; - // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig. - AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); - int64_t matcher2Id = matcher2.id(); - *config.add_atom_matcher() = matcher2; - - AtomMatcher matcher3 = CreateAcquireWakelockAtomMatcher(); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - AtomMatcher matcher4 = CreateReleaseWakelockAtomMatcher(); - int64_t matcher4Id = matcher4.id(); - *config.add_atom_matcher() = matcher4; - - AtomMatcher matcher5 = CreateMoveToForegroundAtomMatcher(); - int64_t matcher5Id = matcher5.id(); - *config.add_atom_matcher() = matcher5; - - AtomMatcher matcher6 = CreateMoveToBackgroundAtomMatcher(); - int64_t matcher6Id = matcher6.id(); - *config.add_atom_matcher() = matcher6; - - AtomMatcher matcher7 = CreateBatteryStateNoneMatcher(); - int64_t matcher7Id = matcher7.id(); - *config.add_atom_matcher() = matcher7; - - AtomMatcher matcher8 = CreateBatteryStateUsbMatcher(); - int64_t matcher8Id = matcher8.id(); - *config.add_atom_matcher() = matcher8; - - Predicate predicate1 = CreateScreenIsOnPredicate(); - int64_t predicate1Id = predicate1.id(); - *config.add_predicate() = predicate1; - - Predicate predicate2 = CreateScreenIsOffPredicate(); - int64_t predicate2Id = predicate2.id(); - *config.add_predicate() = predicate2; - - Predicate predicate3 = CreateDeviceUnpluggedPredicate(); - int64_t predicate3Id = predicate3.id(); - *config.add_predicate() = predicate3; - - Predicate predicate4 = CreateIsInBackgroundPredicate(); - *predicate4.mutable_simple_predicate()->mutable_dimensions() = - CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1}); - int64_t predicate4Id = predicate4.id(); - *config.add_predicate() = predicate4; - - Predicate predicate5 = CreateHoldingWakelockPredicate(); - *predicate5.mutable_simple_predicate()->mutable_dimensions() = - CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - predicate5.mutable_simple_predicate()->set_stop_all(matcher7Id); - int64_t predicate5Id = predicate5.id(); - *config.add_predicate() = predicate5; - - State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321); - int64_t state1Id = state1.id(); - *config.add_state() = state1; - - State state2 = CreateScreenState(); - int64_t state2Id = state2.id(); - *config.add_state() = state2; - - // Add a few duration metrics. - // Will be preserved. - DurationMetric duration1 = - createDurationMetric("DURATION1", predicate5Id, predicate4Id, {state2Id}); - *duration1.mutable_dimensions_in_what() = - CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - MetricConditionLink* link = duration1.add_links(); - link->set_condition(predicate4Id); - *link->mutable_fields_in_what() = - CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - *link->mutable_fields_in_condition() = - CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/); - int64_t duration1Id = duration1.id(); - *config.add_duration_metric() = duration1; - - // Will be replaced. - DurationMetric duration2 = createDurationMetric("DURATION2", predicate1Id, nullopt, {}); - int64_t duration2Id = duration2.id(); - *config.add_duration_metric() = duration2; - - // Will be replaced. - DurationMetric duration3 = createDurationMetric("DURATION3", predicate3Id, nullopt, {state1Id}); - int64_t duration3Id = duration3.id(); - *config.add_duration_metric() = duration3; - - // Will be replaced. - DurationMetric duration4 = createDurationMetric("DURATION4", predicate3Id, predicate2Id, {}); - int64_t duration4Id = duration4.id(); - *config.add_duration_metric() = duration4; - - // Will be deleted. - DurationMetric duration5 = createDurationMetric("DURATION5", predicate2Id, nullopt, {}); - int64_t duration5Id = duration5.id(); - *config.add_duration_metric() = duration5; - - EXPECT_TRUE(initConfig(config)); - - // Make some sliced conditions true. - int uid1 = 10; - int uid2 = 11; - vector<MatchingState> matchingStates(8, MatchingState::kNotMatched); - matchingStates[2] = kMatched; - vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated); - vector<bool> changedCache(5, false); - unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid1}, {"tag"}, "wl1"); - oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers, - conditionCache, changedCache); - EXPECT_TRUE(oldConditionTrackers[4]->isSliced()); - EXPECT_TRUE(changedCache[4]); - EXPECT_EQ(conditionCache[4], ConditionState::kTrue); - oldMetricProducers[0]->onMatchedLogEvent(2, *event.get()); - - fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated); - fill(changedCache.begin(), changedCache.end(), false); - event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid2}, {"tag"}, "wl2"); - oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers, - conditionCache, changedCache); - EXPECT_TRUE(changedCache[4]); - EXPECT_EQ(conditionCache[4], ConditionState::kTrue); - oldMetricProducers[0]->onMatchedLogEvent(2, *event.get()); - - // Used later to ensure the condition wizard is replaced. Get it before doing the update. - // The duration trackers have a pointer to the wizard, and 2 trackers were created above. - sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard; - EXPECT_EQ(oldConditionWizard->getStrongCount(), 8); - - // Replace predicate1, predicate3, and state1. Causes duration2/3/4 to be replaced. - set<int64_t> replacedConditions({predicate1Id, predicate2Id}); - set<int64_t> replacedStates({state1Id}); - - // New duration metric. - DurationMetric duration6 = createDurationMetric("DURATION6", predicate4Id, predicate5Id, {}); - *duration6.mutable_dimensions_in_what() = - CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/); - link = duration6.add_links(); - link->set_condition(predicate5Id); - *link->mutable_fields_in_what() = - CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/); - *link->mutable_fields_in_condition() = - CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - int64_t duration6Id = duration6.id(); - - // Map the matchers and predicates in reverse order to force the indices to change. - const int matcher8Index = 0, matcher7Index = 1, matcher6Index = 2, matcher5Index = 3, - matcher4Index = 4, matcher3Index = 5, matcher2Index = 6, matcher1Index = 7; - std::unordered_map<int64_t, int> newAtomMatchingTrackerMap({{matcher8Id, matcher8Index}, - {matcher7Id, matcher7Index}, - {matcher6Id, matcher6Index}, - {matcher5Id, matcher5Index}, - {matcher4Id, matcher4Index}, - {matcher3Id, matcher3Index}, - {matcher2Id, matcher2Index}, - {matcher1Id, matcher1Index}}); - // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. - vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(8); - reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), - newAtomMatchingTrackers.begin()); - - const int predicate5Index = 0, predicate4Index = 1, predicate3Index = 2, predicate2Index = 3, - predicate1Index = 4; - std::unordered_map<int64_t, int> newConditionTrackerMap({ - {predicate5Id, predicate5Index}, - {predicate4Id, predicate4Index}, - {predicate3Id, predicate3Index}, - {predicate2Id, predicate2Index}, - {predicate1Id, predicate1Index}, - }); - // Use the existing conditionTrackers and reinitialize them to get the initial condition cache. - vector<sp<ConditionTracker>> newConditionTrackers(5); - reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(), - newConditionTrackers.begin()); - vector<Predicate> conditionProtos(5); - reverse_copy(config.predicate().begin(), config.predicate().end(), conditionProtos.begin()); - for (int i = 0; i < newConditionTrackers.size(); i++) { - EXPECT_TRUE(newConditionTrackers[i]->onConfigUpdated( - conditionProtos, i, newConditionTrackers, newAtomMatchingTrackerMap, - newConditionTrackerMap)); - } - vector<bool> cycleTracker(5, false); - fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated); - for (int i = 0; i < newConditionTrackers.size(); i++) { - EXPECT_TRUE(newConditionTrackers[i]->init(conditionProtos, newConditionTrackers, - newConditionTrackerMap, cycleTracker, - conditionCache)); - } - // Predicate5 should be true since 2 uids have wakelocks - EXPECT_EQ(conditionCache, vector({kTrue, kUnknown, kUnknown, kUnknown, kUnknown})); - - StatsdConfig newConfig; - *newConfig.add_duration_metric() = duration6; - const int duration6Index = 0; - *newConfig.add_duration_metric() = duration3; - const int duration3Index = 1; - *newConfig.add_duration_metric() = duration1; - const int duration1Index = 2; - *newConfig.add_duration_metric() = duration4; - const int duration4Index = 3; - *newConfig.add_duration_metric() = duration2; - const int duration2Index = 4; - - for (const Predicate& predicate : conditionProtos) { - *newConfig.add_predicate() = predicate; - } - *newConfig.add_state() = state1; - *newConfig.add_state() = state2; - unordered_map<int64_t, int> stateAtomIdMap; - unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; - map<int64_t, uint64_t> stateProtoHashes; - EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)); - - // Output data structures to validate. - unordered_map<int64_t, int> newMetricProducerMap; - vector<sp<MetricProducer>> newMetricProducers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - set<int64_t> noReportMetricIds; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - vector<int> metricsWithActivation; - set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{}, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, - oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers, - conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); - - unordered_map<int64_t, int> expectedMetricProducerMap = { - {duration1Id, duration1Index}, {duration2Id, duration2Index}, - {duration3Id, duration3Index}, {duration4Id, duration4Index}, - {duration6Id, duration6Index}, - }; - EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); - EXPECT_EQ(replacedMetrics, set<int64_t>({duration2Id, duration3Id, duration4Id})); - // Make sure preserved metrics are the same. - ASSERT_EQ(newMetricProducers.size(), 5); - EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(duration1Id)], - newMetricProducers[newMetricProducerMap.at(duration1Id)]); - - // Make sure replaced metrics are different. - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration2Id)], - newMetricProducers[newMetricProducerMap.at(duration2Id)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration3Id)], - newMetricProducers[newMetricProducerMap.at(duration3Id)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration4Id)], - newMetricProducers[newMetricProducerMap.at(duration4Id)]); - - // Verify the conditionToMetricMap. Note that the "what" is not in this map. - ASSERT_EQ(conditionToMetricMap.size(), 3); - const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index]; - EXPECT_THAT(condition2Metrics, UnorderedElementsAre(duration4Index)); - const vector<int>& condition4Metrics = conditionToMetricMap[predicate4Index]; - EXPECT_THAT(condition4Metrics, UnorderedElementsAre(duration1Index)); - const vector<int>& condition5Metrics = conditionToMetricMap[predicate5Index]; - EXPECT_THAT(condition5Metrics, UnorderedElementsAre(duration6Index)); - - // Verify the trackerToMetricMap. The start/stop/stopall indices from the "what" should be here. - ASSERT_EQ(trackerToMetricMap.size(), 8); - const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; - EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(duration2Index)); - const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index]; - EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(duration2Index)); - const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index]; - EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(duration1Index)); - const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index]; - EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(duration1Index)); - const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index]; - EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(duration6Index)); - const vector<int>& matcher6Metrics = trackerToMetricMap[matcher6Index]; - EXPECT_THAT(matcher6Metrics, UnorderedElementsAre(duration6Index)); - const vector<int>& matcher7Metrics = trackerToMetricMap[matcher7Index]; - EXPECT_THAT(matcher7Metrics, - UnorderedElementsAre(duration1Index, duration3Index, duration4Index)); - const vector<int>& matcher8Metrics = trackerToMetricMap[matcher8Index]; - EXPECT_THAT(matcher8Metrics, UnorderedElementsAre(duration3Index, duration4Index)); - - // Verify event activation/deactivation maps. - ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(metricsWithActivation.size(), 0); - - // Verify tracker indices/ids/conditions are correct. - DurationMetricProducer* durationProducer1 = - static_cast<DurationMetricProducer*>(newMetricProducers[duration1Index].get()); - EXPECT_EQ(durationProducer1->getMetricId(), duration1Id); - EXPECT_EQ(durationProducer1->mConditionTrackerIndex, predicate4Index); - EXPECT_EQ(durationProducer1->mCondition, ConditionState::kUnknown); - EXPECT_EQ(durationProducer1->mStartIndex, matcher3Index); - EXPECT_EQ(durationProducer1->mStopIndex, matcher4Index); - EXPECT_EQ(durationProducer1->mStopAllIndex, matcher7Index); - EXPECT_EQ(durationProducer1->mCurrentSlicedDurationTrackerMap.size(), 2); - for (const auto& durationTrackerIt : durationProducer1->mCurrentSlicedDurationTrackerMap) { - EXPECT_EQ(durationTrackerIt.second->mConditionTrackerIndex, predicate4Index); - } - DurationMetricProducer* durationProducer2 = - static_cast<DurationMetricProducer*>(newMetricProducers[duration2Index].get()); - EXPECT_EQ(durationProducer2->getMetricId(), duration2Id); - EXPECT_EQ(durationProducer2->mConditionTrackerIndex, -1); - EXPECT_EQ(durationProducer2->mCondition, ConditionState::kTrue); - EXPECT_EQ(durationProducer2->mStartIndex, matcher1Index); - EXPECT_EQ(durationProducer2->mStopIndex, matcher2Index); - EXPECT_EQ(durationProducer2->mStopAllIndex, -1); - DurationMetricProducer* durationProducer3 = - static_cast<DurationMetricProducer*>(newMetricProducers[duration3Index].get()); - EXPECT_EQ(durationProducer3->getMetricId(), duration3Id); - EXPECT_EQ(durationProducer3->mConditionTrackerIndex, -1); - EXPECT_EQ(durationProducer3->mCondition, ConditionState::kTrue); - EXPECT_EQ(durationProducer3->mStartIndex, matcher7Index); - EXPECT_EQ(durationProducer3->mStopIndex, matcher8Index); - EXPECT_EQ(durationProducer3->mStopAllIndex, -1); - DurationMetricProducer* durationProducer4 = - static_cast<DurationMetricProducer*>(newMetricProducers[duration4Index].get()); - EXPECT_EQ(durationProducer4->getMetricId(), duration4Id); - EXPECT_EQ(durationProducer4->mConditionTrackerIndex, predicate2Index); - EXPECT_EQ(durationProducer4->mCondition, ConditionState::kUnknown); - EXPECT_EQ(durationProducer4->mStartIndex, matcher7Index); - EXPECT_EQ(durationProducer4->mStopIndex, matcher8Index); - EXPECT_EQ(durationProducer4->mStopAllIndex, -1); - DurationMetricProducer* durationProducer6 = - static_cast<DurationMetricProducer*>(newMetricProducers[duration6Index].get()); - EXPECT_EQ(durationProducer6->getMetricId(), duration6Id); - EXPECT_EQ(durationProducer6->mConditionTrackerIndex, predicate5Index); - // TODO(b/167491517): should this be unknown since the condition is sliced? - EXPECT_EQ(durationProducer6->mCondition, ConditionState::kTrue); - EXPECT_EQ(durationProducer6->mStartIndex, matcher6Index); - EXPECT_EQ(durationProducer6->mStopIndex, matcher5Index); - EXPECT_EQ(durationProducer6->mStopAllIndex, -1); - - sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard; - EXPECT_NE(newConditionWizard, oldConditionWizard); - EXPECT_EQ(newConditionWizard->getStrongCount(), 8); - oldMetricProducers.clear(); - // Only reference to the old wizard should be the one in the test. - EXPECT_EQ(oldConditionWizard->getStrongCount(), 1); -} - -TEST_F(ConfigUpdateTest, TestUpdateValueMetrics) { - StatsdConfig config; - - // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig. - AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); - int64_t matcher2Id = matcher2.id(); - *config.add_atom_matcher() = matcher2; - - AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher(); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - AtomMatcher matcher4 = CreateTemperatureAtomMatcher(); - int64_t matcher4Id = matcher4.id(); - *config.add_atom_matcher() = matcher4; - - AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE); - int64_t matcher5Id = matcher5.id(); - *config.add_atom_matcher() = matcher5; - - Predicate predicate1 = CreateScreenIsOnPredicate(); - int64_t predicate1Id = predicate1.id(); - *config.add_predicate() = predicate1; - - Predicate predicate2 = CreateScreenIsOffPredicate(); - int64_t predicate2Id = predicate2.id(); - *config.add_predicate() = predicate2; - - State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321); - int64_t state1Id = state1.id(); - *config.add_state() = state1; - - State state2 = CreateScreenState(); - int64_t state2Id = state2.id(); - *config.add_state() = state2; - - // Add a few value metrics. - // Note that these will not work as "real" metrics since the value field is always 2. - // Will be preserved. - ValueMetric value1 = createValueMetric("VALUE1", matcher4, predicate1Id, {state1Id}); - int64_t value1Id = value1.id(); - *config.add_value_metric() = value1; - - // Will be replaced - definition change. - ValueMetric value2 = createValueMetric("VALUE2", matcher1, nullopt, {}); - int64_t value2Id = value2.id(); - *config.add_value_metric() = value2; - - // Will be replaced - condition change. - ValueMetric value3 = createValueMetric("VALUE3", matcher5, predicate2Id, {}); - int64_t value3Id = value3.id(); - *config.add_value_metric() = value3; - - // Will be replaced - state change. - ValueMetric value4 = createValueMetric("VALUE4", matcher3, nullopt, {state2Id}); - int64_t value4Id = value4.id(); - *config.add_value_metric() = value4; - - // Will be deleted. - ValueMetric value5 = createValueMetric("VALUE5", matcher2, nullopt, {}); - int64_t value5Id = value5.id(); - *config.add_value_metric() = value5; - - EXPECT_TRUE(initConfig(config)); - - // Used later to ensure the condition wizard is replaced. Get it before doing the update. - sp<EventMatcherWizard> oldMatcherWizard = - static_cast<ValueMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard; - EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6); - - // Change value2, causing it to be replaced. - value2.set_aggregation_type(ValueMetric::AVG); - - // Mark predicate 2 as replaced. Causes value3 to be replaced. - set<int64_t> replacedConditions = {predicate2Id}; - - // Mark state 2 as replaced. Causes value4 to be replaced. - set<int64_t> replacedStates = {state2Id}; - - // New value metric. - ValueMetric value6 = createValueMetric("VALUE6", matcher5, predicate1Id, {state1Id}); - int64_t value6Id = value6.id(); - - // Map the matchers and predicates in reverse order to force the indices to change. - std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; - const int matcher5Index = 0; - newAtomMatchingTrackerMap[matcher5Id] = 0; - const int matcher4Index = 1; - newAtomMatchingTrackerMap[matcher4Id] = 1; - const int matcher3Index = 2; - newAtomMatchingTrackerMap[matcher3Id] = 2; - const int matcher2Index = 3; - newAtomMatchingTrackerMap[matcher2Id] = 3; - const int matcher1Index = 4; - newAtomMatchingTrackerMap[matcher1Id] = 4; - // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. - vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5); - std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), - newAtomMatchingTrackers.begin()); - - std::unordered_map<int64_t, int> newConditionTrackerMap; - const int predicate2Index = 0; - newConditionTrackerMap[predicate2Id] = 0; - const int predicate1Index = 1; - newConditionTrackerMap[predicate1Id] = 1; - // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them. - vector<sp<ConditionTracker>> newConditionTrackers(2); - std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(), - newConditionTrackers.begin()); - // Say that predicate1 & predicate2 is unknown since the initial condition never changed. - vector<ConditionState> conditionCache = {ConditionState::kUnknown, ConditionState::kUnknown}; - - StatsdConfig newConfig; - *newConfig.add_value_metric() = value6; - const int value6Index = 0; - *newConfig.add_value_metric() = value3; - const int value3Index = 1; - *newConfig.add_value_metric() = value1; - const int value1Index = 2; - *newConfig.add_value_metric() = value4; - const int value4Index = 3; - *newConfig.add_value_metric() = value2; - const int value2Index = 4; - - *newConfig.add_state() = state1; - *newConfig.add_state() = state2; - - unordered_map<int64_t, int> stateAtomIdMap; - unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; - map<int64_t, uint64_t> stateProtoHashes; - EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)); - - // Output data structures to validate. - unordered_map<int64_t, int> newMetricProducerMap; - vector<sp<MetricProducer>> newMetricProducers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - set<int64_t> noReportMetricIds; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - vector<int> metricsWithActivation; - set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{}, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, - oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers, - conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); - - unordered_map<int64_t, int> expectedMetricProducerMap = { - {value1Id, value1Index}, {value2Id, value2Index}, {value3Id, value3Index}, - {value4Id, value4Index}, {value6Id, value6Index}, - }; - EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); - EXPECT_EQ(replacedMetrics, set<int64_t>({value2Id, value3Id, value4Id})); - - // Make sure preserved metrics are the same. - ASSERT_EQ(newMetricProducers.size(), 5); - EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(value1Id)], - newMetricProducers[newMetricProducerMap.at(value1Id)]); - - // Make sure replaced metrics are different. - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value2Id)], - newMetricProducers[newMetricProducerMap.at(value2Id)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value3Id)], - newMetricProducers[newMetricProducerMap.at(value3Id)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value4Id)], - newMetricProducers[newMetricProducerMap.at(value4Id)]); - - // Verify the conditionToMetricMap. - ASSERT_EQ(conditionToMetricMap.size(), 2); - const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index]; - EXPECT_THAT(condition1Metrics, UnorderedElementsAre(value1Index, value6Index)); - const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index]; - EXPECT_THAT(condition2Metrics, UnorderedElementsAre(value3Index)); - - // Verify the trackerToMetricMap. - ASSERT_EQ(trackerToMetricMap.size(), 4); - const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; - EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(value2Index)); - const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index]; - EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(value4Index)); - const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index]; - EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(value1Index)); - const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index]; - EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(value3Index, value6Index)); - - // Verify event activation/deactivation maps. - ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(metricsWithActivation.size(), 0); - - // Verify tracker indices/ids/conditions/states are correct. - ValueMetricProducer* valueProducer1 = - static_cast<ValueMetricProducer*>(newMetricProducers[value1Index].get()); - EXPECT_EQ(valueProducer1->getMetricId(), value1Id); - EXPECT_EQ(valueProducer1->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(valueProducer1->mCondition, ConditionState::kUnknown); - EXPECT_EQ(valueProducer1->mWhatMatcherIndex, matcher4Index); - ValueMetricProducer* valueProducer2 = - static_cast<ValueMetricProducer*>(newMetricProducers[value2Index].get()); - EXPECT_EQ(valueProducer2->getMetricId(), value2Id); - EXPECT_EQ(valueProducer2->mConditionTrackerIndex, -1); - EXPECT_EQ(valueProducer2->mCondition, ConditionState::kTrue); - EXPECT_EQ(valueProducer2->mWhatMatcherIndex, matcher1Index); - ValueMetricProducer* valueProducer3 = - static_cast<ValueMetricProducer*>(newMetricProducers[value3Index].get()); - EXPECT_EQ(valueProducer3->getMetricId(), value3Id); - EXPECT_EQ(valueProducer3->mConditionTrackerIndex, predicate2Index); - EXPECT_EQ(valueProducer3->mCondition, ConditionState::kUnknown); - EXPECT_EQ(valueProducer3->mWhatMatcherIndex, matcher5Index); - ValueMetricProducer* valueProducer4 = - static_cast<ValueMetricProducer*>(newMetricProducers[value4Index].get()); - EXPECT_EQ(valueProducer4->getMetricId(), value4Id); - EXPECT_EQ(valueProducer4->mConditionTrackerIndex, -1); - EXPECT_EQ(valueProducer4->mCondition, ConditionState::kTrue); - EXPECT_EQ(valueProducer4->mWhatMatcherIndex, matcher3Index); - ValueMetricProducer* valueProducer6 = - static_cast<ValueMetricProducer*>(newMetricProducers[value6Index].get()); - EXPECT_EQ(valueProducer6->getMetricId(), value6Id); - EXPECT_EQ(valueProducer6->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(valueProducer6->mCondition, ConditionState::kUnknown); - EXPECT_EQ(valueProducer6->mWhatMatcherIndex, matcher5Index); - - sp<EventMatcherWizard> newMatcherWizard = valueProducer1->mEventMatcherWizard; - EXPECT_NE(newMatcherWizard, oldMatcherWizard); - EXPECT_EQ(newMatcherWizard->getStrongCount(), 6); - oldMetricProducers.clear(); - // Only reference to the old wizard should be the one in the test. - EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1); -} - -TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) { - StatsdConfig config; - // Add atom matchers - AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); - int64_t matcher2Id = matcher2.id(); - *config.add_atom_matcher() = matcher2; - - AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher(); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher(); - int64_t matcher4Id = matcher4.id(); - *config.add_atom_matcher() = matcher4; - - // Add an event metric with multiple activations. - EventMetric event1 = createEventMetric("EVENT1", matcher1Id, nullopt); - int64_t event1Id = event1.id(); - *config.add_event_metric() = event1; - - int64_t matcher2TtlSec = 2, matcher3TtlSec = 3, matcher4TtlSec = 4; - MetricActivation metricActivation; - metricActivation.set_metric_id(event1Id); - EventActivation* activation = metricActivation.add_event_activation(); - activation->set_atom_matcher_id(matcher2Id); - activation->set_ttl_seconds(matcher2TtlSec); - activation->set_activation_type(ACTIVATE_IMMEDIATELY); - activation->set_deactivation_atom_matcher_id(matcher1Id); - activation = metricActivation.add_event_activation(); - activation->set_atom_matcher_id(matcher3Id); - activation->set_ttl_seconds(matcher3TtlSec); - activation->set_activation_type(ACTIVATE_ON_BOOT); - activation->set_deactivation_atom_matcher_id(matcher1Id); - activation = metricActivation.add_event_activation(); - activation->set_atom_matcher_id(matcher4Id); - activation->set_ttl_seconds(matcher4TtlSec); - activation->set_activation_type(ACTIVATE_IMMEDIATELY); - activation->set_deactivation_atom_matcher_id(matcher2Id); - *config.add_metric_activation() = metricActivation; - - EXPECT_TRUE(initConfig(config)); - - // Activate some of the event activations. - ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id); - int64_t matcher2StartNs = 12345; - oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher2Id], matcher2StartNs); - int64_t matcher3StartNs = 23456; - oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher3Id], matcher3StartNs); - EXPECT_TRUE(oldMetricProducers[0]->isActive()); - - // Map the matchers and predicates in reverse order to force the indices to change. - std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; - const int matcher4Index = 0; - newAtomMatchingTrackerMap[matcher4Id] = 0; - const int matcher3Index = 1; - newAtomMatchingTrackerMap[matcher3Id] = 1; - const int matcher2Index = 2; - newAtomMatchingTrackerMap[matcher2Id] = 2; - const int matcher1Index = 3; - newAtomMatchingTrackerMap[matcher1Id] = 3; - // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. - vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(4); - std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), - newAtomMatchingTrackers.begin()); - set<int64_t> replacedMatchers; - - unordered_map<int64_t, int> newConditionTrackerMap; - vector<sp<ConditionTracker>> newConditionTrackers; - set<int64_t> replacedConditions; - vector<ConditionState> conditionCache; - unordered_map<int64_t, int> newMetricProducerMap; - vector<sp<MetricProducer>> newMetricProducers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - set<int64_t> noReportMetricIds; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - vector<int> metricsWithActivation; - set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); - - // Verify event activation/deactivation maps. - ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 3); - EXPECT_THAT(activationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0)); - EXPECT_THAT(activationAtomTrackerToMetricMap[matcher3Index], UnorderedElementsAre(0)); - EXPECT_THAT(activationAtomTrackerToMetricMap[matcher4Index], UnorderedElementsAre(0)); - ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 2); - EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher1Index], UnorderedElementsAre(0, 0)); - EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0)); - ASSERT_EQ(metricsWithActivation.size(), 1); - EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(0)); - - // Verify mEventActivation and mEventDeactivation map of the producer. - sp<MetricProducer> producer = newMetricProducers[0]; - EXPECT_TRUE(producer->isActive()); - ASSERT_EQ(producer->mEventActivationMap.size(), 3); - shared_ptr<Activation> matcher2Activation = producer->mEventActivationMap[matcher2Index]; - EXPECT_EQ(matcher2Activation->ttl_ns, matcher2TtlSec * NS_PER_SEC); - EXPECT_EQ(matcher2Activation->activationType, ACTIVATE_IMMEDIATELY); - EXPECT_EQ(matcher2Activation->state, kActive); - EXPECT_EQ(matcher2Activation->start_ns, matcher2StartNs); - shared_ptr<Activation> matcher3Activation = producer->mEventActivationMap[matcher3Index]; - EXPECT_EQ(matcher3Activation->ttl_ns, matcher3TtlSec * NS_PER_SEC); - EXPECT_EQ(matcher3Activation->activationType, ACTIVATE_ON_BOOT); - EXPECT_EQ(matcher3Activation->state, kActiveOnBoot); - shared_ptr<Activation> matcher4Activation = producer->mEventActivationMap[matcher4Index]; - EXPECT_EQ(matcher4Activation->ttl_ns, matcher4TtlSec * NS_PER_SEC); - EXPECT_EQ(matcher4Activation->activationType, ACTIVATE_IMMEDIATELY); - EXPECT_EQ(matcher4Activation->state, kNotActive); - - ASSERT_EQ(producer->mEventDeactivationMap.size(), 2); - EXPECT_THAT(producer->mEventDeactivationMap[matcher1Index], - UnorderedElementsAre(matcher2Activation, matcher3Activation)); - EXPECT_THAT(producer->mEventDeactivationMap[matcher2Index], - UnorderedElementsAre(matcher4Activation)); -} - -TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) { - StatsdConfig config; - // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig - AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); - int64_t matcher1Id = matcher1.id(); - *config.add_atom_matcher() = matcher1; - - AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); - int64_t matcher2Id = matcher2.id(); - *config.add_atom_matcher() = matcher2; - - AtomMatcher matcher3 = CreateTemperatureAtomMatcher(); - int64_t matcher3Id = matcher3.id(); - *config.add_atom_matcher() = matcher3; - - Predicate predicate1 = CreateScreenIsOnPredicate(); - int64_t predicate1Id = predicate1.id(); - *config.add_predicate() = predicate1; - - // Add a few count metrics. - // Will be preserved. - CountMetric countMetric = createCountMetric("COUNT1", matcher1Id, predicate1Id, {}); - int64_t countMetricId = countMetric.id(); - *config.add_count_metric() = countMetric; - - // Will be replaced since matcher2 is replaced. - EventMetric eventMetric = createEventMetric("EVENT1", matcher2Id, nullopt); - int64_t eventMetricId = eventMetric.id(); - *config.add_event_metric() = eventMetric; - - // Will be replaced because the definition changes - a predicate is added. - GaugeMetric gaugeMetric = createGaugeMetric("GAUGE1", matcher3Id, - GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt); - int64_t gaugeMetricId = gaugeMetric.id(); - *config.add_gauge_metric() = gaugeMetric; - - // Preserved. - ValueMetric valueMetric = createValueMetric("VALUE1", matcher3, predicate1Id, {}); - int64_t valueMetricId = valueMetric.id(); - *config.add_value_metric() = valueMetric; - - // Preserved. - DurationMetric durationMetric = createDurationMetric("DURATION1", predicate1Id, nullopt, {}); - int64_t durationMetricId = durationMetric.id(); - *config.add_duration_metric() = durationMetric; - - EXPECT_TRUE(initConfig(config)); - - // Used later to ensure the condition wizard is replaced. Get it before doing the update. - sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard; - EXPECT_EQ(oldConditionWizard->getStrongCount(), 6); - - // Mark matcher 2 as replaced. Causes eventMetric to be replaced. - set<int64_t> replacedMatchers; - replacedMatchers.insert(matcher2Id); - - // Add predicate1 as a predicate on gaugeMetric, causing it to be replaced. - gaugeMetric.set_condition(predicate1Id); - - // Map the matchers and predicates in reverse order to force the indices to change. - std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; - const int matcher3Index = 0; - newAtomMatchingTrackerMap[matcher3Id] = 0; - const int matcher2Index = 1; - newAtomMatchingTrackerMap[matcher2Id] = 1; - const int matcher1Index = 2; - newAtomMatchingTrackerMap[matcher1Id] = 2; - // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. - vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(3); - std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), - newAtomMatchingTrackers.begin()); - - std::unordered_map<int64_t, int> newConditionTrackerMap; - const int predicate1Index = 0; - newConditionTrackerMap[predicate1Id] = 0; - // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them. - vector<sp<ConditionTracker>> newConditionTrackers(1); - std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(), - newConditionTrackers.begin()); - vector<ConditionState> conditionCache = {ConditionState::kUnknown}; - - // The order matters. we parse in the order of: count, duration, event, value, gauge. - StatsdConfig newConfig; - *newConfig.add_count_metric() = countMetric; - const int countMetricIndex = 0; - *newConfig.add_duration_metric() = durationMetric; - const int durationMetricIndex = 1; - *newConfig.add_event_metric() = eventMetric; - const int eventMetricIndex = 2; - *newConfig.add_value_metric() = valueMetric; - const int valueMetricIndex = 3; - *newConfig.add_gauge_metric() = gaugeMetric; - const int gaugeMetricIndex = 4; - - // Add the predicate since duration metric needs it. - *newConfig.add_predicate() = predicate1; - - // Output data structures to validate. - unordered_map<int64_t, int> newMetricProducerMap; - vector<sp<MetricProducer>> newMetricProducers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - set<int64_t> noReportMetricIds; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - vector<int> metricsWithActivation; - set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{}, - newConditionTrackers, conditionCache, /*stateAtomIdMap*/ {}, /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); - - unordered_map<int64_t, int> expectedMetricProducerMap = { - {countMetricId, countMetricIndex}, {durationMetricId, durationMetricIndex}, - {eventMetricId, eventMetricIndex}, {valueMetricId, valueMetricIndex}, - {gaugeMetricId, gaugeMetricIndex}, - }; - EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); - - EXPECT_EQ(replacedMetrics, set<int64_t>({eventMetricId, gaugeMetricId})); - - // Make sure preserved metrics are the same. - ASSERT_EQ(newMetricProducers.size(), 5); - EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)], - newMetricProducers[newMetricProducerMap.at(countMetricId)]); - EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(durationMetricId)], - newMetricProducers[newMetricProducerMap.at(durationMetricId)]); - EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(valueMetricId)], - newMetricProducers[newMetricProducerMap.at(valueMetricId)]); - - // Make sure replaced metrics are different. - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)], - newMetricProducers[newMetricProducerMap.at(eventMetricId)]); - EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gaugeMetricId)], - newMetricProducers[newMetricProducerMap.at(gaugeMetricId)]); - - // Verify the conditionToMetricMap. - ASSERT_EQ(conditionToMetricMap.size(), 1); - const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index]; - EXPECT_THAT(condition1Metrics, - UnorderedElementsAre(countMetricIndex, gaugeMetricIndex, valueMetricIndex)); - - // Verify the trackerToMetricMap. - ASSERT_EQ(trackerToMetricMap.size(), 3); - const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; - EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex, durationMetricIndex)); - const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index]; - EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex, durationMetricIndex)); - const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index]; - EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex, valueMetricIndex)); - - // Verify event activation/deactivation maps. - ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0); - ASSERT_EQ(metricsWithActivation.size(), 0); - - // Verify tracker indices/ids/conditions are correct. - EXPECT_EQ(newMetricProducers[countMetricIndex]->getMetricId(), countMetricId); - EXPECT_EQ(newMetricProducers[countMetricIndex]->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(newMetricProducers[countMetricIndex]->mCondition, ConditionState::kUnknown); - EXPECT_EQ(newMetricProducers[durationMetricIndex]->getMetricId(), durationMetricId); - EXPECT_EQ(newMetricProducers[durationMetricIndex]->mConditionTrackerIndex, -1); - EXPECT_EQ(newMetricProducers[durationMetricIndex]->mCondition, ConditionState::kTrue); - EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId); - EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1); - EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue); - EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->getMetricId(), gaugeMetricId); - EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mConditionTrackerIndex, predicate1Index); - EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mCondition, ConditionState::kUnknown); - - sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard; - EXPECT_NE(newConditionWizard, oldConditionWizard); - EXPECT_EQ(newConditionWizard->getStrongCount(), 6); - oldMetricProducers.clear(); - // Only reference to the old wizard should be the one in the test. - EXPECT_EQ(oldConditionWizard->getStrongCount(), 1); -} - -TEST_F(ConfigUpdateTest, TestAlertPreserve) { - StatsdConfig config; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - *config.add_count_metric() = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {}); - - Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1); - *config.add_alert() = alert; - EXPECT_TRUE(initConfig(config)); - - UpdateStatus updateStatus = UPDATE_UNKNOWN; - EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers, - /*replacedMetrics*/ {}, updateStatus)); - EXPECT_EQ(updateStatus, UPDATE_PRESERVE); -} - -TEST_F(ConfigUpdateTest, TestAlertMetricChanged) { - StatsdConfig config; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - CountMetric metric = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {}); - *config.add_count_metric() = metric; - - Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1); - *config.add_alert() = alert; - EXPECT_TRUE(initConfig(config)); - - UpdateStatus updateStatus = UPDATE_UNKNOWN; - EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers, - /*replacedMetrics*/ {metric.id()}, updateStatus)); - EXPECT_EQ(updateStatus, UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestAlertDefinitionChanged) { - StatsdConfig config; - AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); - *config.add_atom_matcher() = whatMatcher; - - *config.add_count_metric() = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {}); - - Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1); - *config.add_alert() = alert; - EXPECT_TRUE(initConfig(config)); - - alert.set_num_buckets(2); - - UpdateStatus updateStatus = UPDATE_UNKNOWN; - EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers, - /*replacedMetrics*/ {}, updateStatus)); - EXPECT_EQ(updateStatus, UPDATE_REPLACE); -} - -TEST_F(ConfigUpdateTest, TestUpdateAlerts) { - StatsdConfig config; - // Add atom matchers/predicates/metrics. These are mostly needed for initStatsdConfig - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - *config.add_predicate() = CreateScreenIsOnPredicate(); - - CountMetric countMetric = createCountMetric("COUNT1", config.atom_matcher(0).id(), nullopt, {}); - int64_t countMetricId = countMetric.id(); - *config.add_count_metric() = countMetric; - - DurationMetric durationMetric = - createDurationMetric("DURATION1", config.predicate(0).id(), nullopt, {}); - int64_t durationMetricId = durationMetric.id(); - *config.add_duration_metric() = durationMetric; - - // Add alerts. - // Preserved. - Alert alert1 = createAlert("Alert1", durationMetricId, /*buckets*/ 1, /*triggerSum*/ 5000); - int64_t alert1Id = alert1.id(); - *config.add_alert() = alert1; - - // Replaced. - Alert alert2 = createAlert("Alert2", countMetricId, /*buckets*/ 1, /*triggerSum*/ 2); - int64_t alert2Id = alert2.id(); - *config.add_alert() = alert2; - - // Replaced. - Alert alert3 = createAlert("Alert3", durationMetricId, /*buckets*/ 3, /*triggerSum*/ 5000); - int64_t alert3Id = alert3.id(); - *config.add_alert() = alert3; - - // Add Subscriptions. - Subscription subscription1 = createSubscription("S1", Subscription::ALERT, alert1Id); - *config.add_subscription() = subscription1; - Subscription subscription2 = createSubscription("S2", Subscription::ALERT, alert1Id); - *config.add_subscription() = subscription2; - Subscription subscription3 = createSubscription("S3", Subscription::ALERT, alert2Id); - *config.add_subscription() = subscription3; - - EXPECT_TRUE(initConfig(config)); - - // Add a duration tracker to the duration metric to ensure durationTrackers are updated - // with the proper anomalyTrackers. - unique_ptr<LogEvent> event = CreateScreenStateChangedEvent( - timeBaseNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - oldMetricProducers[1]->onMatchedLogEvent(0, *event.get()); - - // Change the count metric. Causes alert2 to be replaced. - config.mutable_count_metric(0)->set_bucket(ONE_DAY); - // Change num buckets on alert3, causing replacement. - alert3.set_num_buckets(5); - - // New alert. - Alert alert4 = createAlert("Alert4", durationMetricId, /*buckets*/ 3, /*triggerSum*/ 10000); - int64_t alert4Id = alert4.id(); - - // Move subscription2 to be on alert2 and make a new subscription. - subscription2.set_rule_id(alert2Id); - Subscription subscription4 = createSubscription("S4", Subscription::ALERT, alert2Id); - - // Create the new config. Modify the old one to avoid adding the matchers/predicates. - // Add alerts in different order so the map is changed. - config.clear_alert(); - *config.add_alert() = alert4; - const int alert4Index = 0; - *config.add_alert() = alert3; - const int alert3Index = 1; - *config.add_alert() = alert1; - const int alert1Index = 2; - *config.add_alert() = alert2; - const int alert2Index = 3; - - // Subscription3 is removed. - config.clear_subscription(); - *config.add_subscription() = subscription4; - *config.add_subscription() = subscription2; - *config.add_subscription() = subscription1; - - // Output data structures from update metrics. Don't care about the outputs besides - // replacedMetrics, but need to do this so that the metrics clear their anomaly trackers. - unordered_map<int64_t, int> newMetricProducerMap; - vector<sp<MetricProducer>> newMetricProducers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - set<int64_t> noReportMetricIds; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - vector<int> metricsWithActivation; - set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, oldAtomMatchingTrackerMap, /*replacedMatchers*/ {}, - oldAtomMatchingTrackers, oldConditionTrackerMap, /*replacedConditions=*/{}, - oldConditionTrackers, {ConditionState::kUnknown}, /*stateAtomIdMap*/ {}, - /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); - - EXPECT_EQ(replacedMetrics, set<int64_t>({countMetricId})); - - unordered_map<int64_t, int> newAlertTrackerMap; - vector<sp<AnomalyTracker>> newAnomalyTrackers; - EXPECT_TRUE(updateAlerts(config, newMetricProducerMap, replacedMetrics, oldAlertTrackerMap, - oldAnomalyTrackers, anomalyAlarmMonitor, newMetricProducers, - newAlertTrackerMap, newAnomalyTrackers)); - - unordered_map<int64_t, int> expectedAlertMap = { - {alert1Id, alert1Index}, - {alert2Id, alert2Index}, - {alert3Id, alert3Index}, - {alert4Id, alert4Index}, - }; - EXPECT_THAT(newAlertTrackerMap, ContainerEq(expectedAlertMap)); - - // Make sure preserved alerts are the same. - ASSERT_EQ(newAnomalyTrackers.size(), 4); - EXPECT_EQ(oldAnomalyTrackers[oldAlertTrackerMap.at(alert1Id)], - newAnomalyTrackers[newAlertTrackerMap.at(alert1Id)]); - - // Make sure replaced alerts are different. - EXPECT_NE(oldAnomalyTrackers[oldAlertTrackerMap.at(alert2Id)], - newAnomalyTrackers[newAlertTrackerMap.at(alert2Id)]); - EXPECT_NE(oldAnomalyTrackers[oldAlertTrackerMap.at(alert3Id)], - newAnomalyTrackers[newAlertTrackerMap.at(alert3Id)]); - - // Verify the alerts have the correct anomaly trackers. - ASSERT_EQ(newMetricProducers.size(), 2); - EXPECT_THAT(newMetricProducers[0]->mAnomalyTrackers, - UnorderedElementsAre(newAnomalyTrackers[alert2Index])); - // For durationMetric, make sure the duration trackers get the updated anomalyTrackers. - DurationMetricProducer* durationProducer = - static_cast<DurationMetricProducer*>(newMetricProducers[1].get()); - EXPECT_THAT( - durationProducer->mAnomalyTrackers, - UnorderedElementsAre(newAnomalyTrackers[alert1Index], newAnomalyTrackers[alert3Index], - newAnomalyTrackers[alert4Index])); - ASSERT_EQ(durationProducer->mCurrentSlicedDurationTrackerMap.size(), 1); - for (const auto& durationTrackerIt : durationProducer->mCurrentSlicedDurationTrackerMap) { - EXPECT_EQ(durationTrackerIt.second->mAnomalyTrackers, durationProducer->mAnomalyTrackers); - } - - // Verify alerts have the correct subscriptions. Use subscription id as proxy for equivalency. - vector<int64_t> alert1Subscriptions; - for (const Subscription& subscription : newAnomalyTrackers[alert1Index]->mSubscriptions) { - alert1Subscriptions.push_back(subscription.id()); - } - EXPECT_THAT(alert1Subscriptions, UnorderedElementsAre(subscription1.id())); - vector<int64_t> alert2Subscriptions; - for (const Subscription& subscription : newAnomalyTrackers[alert2Index]->mSubscriptions) { - alert2Subscriptions.push_back(subscription.id()); - } - EXPECT_THAT(alert2Subscriptions, UnorderedElementsAre(subscription2.id(), subscription4.id())); - EXPECT_THAT(newAnomalyTrackers[alert3Index]->mSubscriptions, IsEmpty()); - EXPECT_THAT(newAnomalyTrackers[alert4Index]->mSubscriptions, IsEmpty()); -} - -TEST_F(ConfigUpdateTest, TestUpdateAlarms) { - StatsdConfig config; - // Add alarms. - Alarm alarm1 = createAlarm("Alarm1", /*offset*/ 1 * MS_PER_SEC, /*period*/ 50 * MS_PER_SEC); - int64_t alarm1Id = alarm1.id(); - *config.add_alarm() = alarm1; - - Alarm alarm2 = createAlarm("Alarm2", /*offset*/ 1 * MS_PER_SEC, /*period*/ 2000 * MS_PER_SEC); - int64_t alarm2Id = alarm2.id(); - *config.add_alarm() = alarm2; - - Alarm alarm3 = createAlarm("Alarm3", /*offset*/ 10 * MS_PER_SEC, /*period*/ 5000 * MS_PER_SEC); - int64_t alarm3Id = alarm3.id(); - *config.add_alarm() = alarm3; - - // Add Subscriptions. - Subscription subscription1 = createSubscription("S1", Subscription::ALARM, alarm1Id); - *config.add_subscription() = subscription1; - Subscription subscription2 = createSubscription("S2", Subscription::ALARM, alarm1Id); - *config.add_subscription() = subscription2; - Subscription subscription3 = createSubscription("S3", Subscription::ALARM, alarm2Id); - *config.add_subscription() = subscription3; - - EXPECT_TRUE(initConfig(config)); - - ASSERT_EQ(oldAlarmTrackers.size(), 3); - // Config is created at statsd start time, so just add the offsets. - EXPECT_EQ(oldAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1); - EXPECT_EQ(oldAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1); - EXPECT_EQ(oldAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10); - - // Change alarm2/alarm3. - config.mutable_alarm(1)->set_offset_millis(5 * MS_PER_SEC); - config.mutable_alarm(2)->set_period_millis(10000 * MS_PER_SEC); - - // Move subscription2 to be on alarm2 and make a new subscription. - config.mutable_subscription(1)->set_rule_id(alarm2Id); - Subscription subscription4 = createSubscription("S4", Subscription::ALARM, alarm1Id); - *config.add_subscription() = subscription4; - - // Update time is 2 seconds after the base time. - int64_t currentTimeNs = timeBaseNs + 2 * NS_PER_SEC; - vector<sp<AlarmTracker>> newAlarmTrackers; - EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, - newAlarmTrackers)); - - ASSERT_EQ(newAlarmTrackers.size(), 3); - // Config is updated 2 seconds after statsd start - // The offset has passed for alarm1, but not for alarms 2/3. - EXPECT_EQ(newAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1 + 50); - EXPECT_EQ(newAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 5); - EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10); - - // Verify alarms have the correct subscriptions. Use subscription id as proxy for equivalency. - vector<int64_t> alarm1Subscriptions; - for (const Subscription& subscription : newAlarmTrackers[0]->mSubscriptions) { - alarm1Subscriptions.push_back(subscription.id()); - } - EXPECT_THAT(alarm1Subscriptions, UnorderedElementsAre(subscription1.id(), subscription4.id())); - vector<int64_t> alarm2Subscriptions; - for (const Subscription& subscription : newAlarmTrackers[1]->mSubscriptions) { - alarm2Subscriptions.push_back(subscription.id()); - } - EXPECT_THAT(alarm2Subscriptions, UnorderedElementsAre(subscription2.id(), subscription3.id())); - EXPECT_THAT(newAlarmTrackers[2]->mSubscriptions, IsEmpty()); - - // Verify the alarm monitor is updated accordingly once the old alarms are removed. - // Alarm2 fires the earliest. - oldAlarmTrackers.clear(); - EXPECT_EQ(periodicAlarmMonitor->getRegisteredAlarmTimeSec(), timeBaseNs / NS_PER_SEC + 5); - - // Do another update 60 seconds after config creation time, after the offsets of each alarm. - currentTimeNs = timeBaseNs + 60 * NS_PER_SEC; - newAlarmTrackers.clear(); - EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, - newAlarmTrackers)); - - ASSERT_EQ(newAlarmTrackers.size(), 3); - // Config is updated one minute after statsd start. - // Two periods have passed for alarm 1, one has passed for alarms2/3. - EXPECT_EQ(newAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1 + 2 * 50); - EXPECT_EQ(newAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 5 + 2000); - EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10 + 10000); -} - -} // namespace statsd -} // namespace os -} // namespace android - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp deleted file mode 100644 index 9e2350b33018..000000000000 --- a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp +++ /dev/null @@ -1,890 +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. - -#include "src/metrics/parsing_utils/metrics_manager_util.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <private/android_filesystem_config.h> -#include <stdio.h> - -#include <set> -#include <unordered_map> -#include <vector> - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "src/condition/ConditionTracker.h" -#include "src/matchers/AtomMatchingTracker.h" -#include "src/metrics/CountMetricProducer.h" -#include "src/metrics/DurationMetricProducer.h" -#include "src/metrics/GaugeMetricProducer.h" -#include "src/metrics/MetricProducer.h" -#include "src/metrics/ValueMetricProducer.h" -#include "src/state/StateManager.h" -#include "tests/metrics/metrics_test_helper.h" -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using android::os::statsd::Predicate; -using std::map; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -namespace { -const ConfigKey kConfigKey(0, 12345); -const long kAlertId = 3; - -const long timeBaseSec = 1000; - -StatsdConfig buildGoodConfig() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_OFF")); - - simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF")); - - AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(StringToId("SCREEN_IS_ON")); - combination->add_matcher(StringToId("SCREEN_IS_OFF")); - - CountMetric* metric = config.add_count_metric(); - metric->set_id(3); - metric->set_what(StringToId("SCREEN_IS_ON")); - metric->set_bucket(ONE_MINUTE); - metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/); - metric->mutable_dimensions_in_what()->add_child()->set_field(1); - - config.add_no_report_metric(3); - - auto alert = config.add_alert(); - alert->set_id(kAlertId); - alert->set_metric_id(3); - alert->set_num_buckets(10); - alert->set_refractory_period_secs(100); - alert->set_trigger_if_sum_gt(100); - return config; -} - -StatsdConfig buildCircleMatchers() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF")); - - AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(StringToId("SCREEN_IS_ON")); - // Circle dependency - combination->add_matcher(StringToId("SCREEN_ON_OR_OFF")); - - return config; -} - -StatsdConfig buildAlertWithUnknownMetric() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - CountMetric* metric = config.add_count_metric(); - metric->set_id(3); - metric->set_what(StringToId("SCREEN_IS_ON")); - metric->set_bucket(ONE_MINUTE); - metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/); - metric->mutable_dimensions_in_what()->add_child()->set_field(1); - - auto alert = config.add_alert(); - alert->set_id(3); - alert->set_metric_id(2); - alert->set_num_buckets(10); - alert->set_refractory_period_secs(100); - alert->set_trigger_if_sum_gt(100); - return config; -} - -StatsdConfig buildMissingMatchers() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF")); - - AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(StringToId("SCREEN_IS_ON")); - // undefined matcher - combination->add_matcher(StringToId("ABC")); - - return config; -} - -StatsdConfig buildMissingPredicate() { - StatsdConfig config; - config.set_id(12345); - - CountMetric* metric = config.add_count_metric(); - metric->set_id(3); - metric->set_what(StringToId("SCREEN_EVENT")); - metric->set_bucket(ONE_MINUTE); - metric->set_condition(StringToId("SOME_CONDITION")); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_EVENT")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2); - - return config; -} - -StatsdConfig buildDimensionMetricsWithMultiTags() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("BATTERY_VERY_LOW")); - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW")); - simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(3); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("BATTERY_LOW")); - - AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(StringToId("BATTERY_VERY_LOW")); - combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW")); - - // Count process state changes, slice by uid, while SCREEN_IS_OFF - CountMetric* metric = config.add_count_metric(); - metric->set_id(3); - metric->set_what(StringToId("BATTERY_LOW")); - metric->set_bucket(ONE_MINUTE); - // This case is interesting. We want to dimension across two atoms. - metric->mutable_dimensions_in_what()->add_child()->set_field(1); - - auto alert = config.add_alert(); - alert->set_id(kAlertId); - alert->set_metric_id(3); - alert->set_num_buckets(10); - alert->set_refractory_period_secs(100); - alert->set_trigger_if_sum_gt(100); - return config; -} - -StatsdConfig buildCirclePredicates() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_OFF")); - - simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); - - auto condition = config.add_predicate(); - condition->set_id(StringToId("SCREEN_IS_ON")); - SimplePredicate* simplePredicate = condition->mutable_simple_predicate(); - simplePredicate->set_start(StringToId("SCREEN_IS_ON")); - simplePredicate->set_stop(StringToId("SCREEN_IS_OFF")); - - condition = config.add_predicate(); - condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF")); - - Predicate_Combination* combination = condition->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_predicate(StringToId("SCREEN_IS_ON")); - combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF")); - - return config; -} - -StatsdConfig buildConfigWithDifferentPredicates() { - StatsdConfig config; - config.set_id(12345); - - auto pulledAtomMatcher = - CreateSimpleAtomMatcher("SUBSYSTEM_SLEEP", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - auto screenOnAtomMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = screenOnAtomMatcher; - auto screenOffAtomMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = screenOffAtomMatcher; - auto batteryNoneAtomMatcher = CreateBatteryStateNoneMatcher(); - *config.add_atom_matcher() = batteryNoneAtomMatcher; - auto batteryUsbAtomMatcher = CreateBatteryStateUsbMatcher(); - *config.add_atom_matcher() = batteryUsbAtomMatcher; - - // Simple condition with InitialValue set to default (unknown). - auto screenOnUnknownPredicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = screenOnUnknownPredicate; - - // Simple condition with InitialValue set to false. - auto screenOnFalsePredicate = config.add_predicate(); - screenOnFalsePredicate->set_id(StringToId("ScreenIsOnInitialFalse")); - SimplePredicate* simpleScreenOnFalsePredicate = - screenOnFalsePredicate->mutable_simple_predicate(); - simpleScreenOnFalsePredicate->set_start(screenOnAtomMatcher.id()); - simpleScreenOnFalsePredicate->set_stop(screenOffAtomMatcher.id()); - simpleScreenOnFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE); - - // Simple condition with InitialValue set to false. - auto onBatteryFalsePredicate = config.add_predicate(); - onBatteryFalsePredicate->set_id(StringToId("OnBatteryInitialFalse")); - SimplePredicate* simpleOnBatteryFalsePredicate = - onBatteryFalsePredicate->mutable_simple_predicate(); - simpleOnBatteryFalsePredicate->set_start(batteryNoneAtomMatcher.id()); - simpleOnBatteryFalsePredicate->set_stop(batteryUsbAtomMatcher.id()); - simpleOnBatteryFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE); - - // Combination condition with both simple condition InitialValues set to false. - auto screenOnFalseOnBatteryFalsePredicate = config.add_predicate(); - screenOnFalseOnBatteryFalsePredicate->set_id(StringToId("ScreenOnFalseOnBatteryFalse")); - screenOnFalseOnBatteryFalsePredicate->mutable_combination()->set_operation( - LogicalOperation::AND); - addPredicateToPredicateCombination(*screenOnFalsePredicate, - screenOnFalseOnBatteryFalsePredicate); - addPredicateToPredicateCombination(*onBatteryFalsePredicate, - screenOnFalseOnBatteryFalsePredicate); - - // Combination condition with one simple condition InitialValue set to unknown and one set to - // false. - auto screenOnUnknownOnBatteryFalsePredicate = config.add_predicate(); - screenOnUnknownOnBatteryFalsePredicate->set_id(StringToId("ScreenOnUnknowneOnBatteryFalse")); - screenOnUnknownOnBatteryFalsePredicate->mutable_combination()->set_operation( - LogicalOperation::AND); - addPredicateToPredicateCombination(screenOnUnknownPredicate, - screenOnUnknownOnBatteryFalsePredicate); - addPredicateToPredicateCombination(*onBatteryFalsePredicate, - screenOnUnknownOnBatteryFalsePredicate); - - // Simple condition metric with initial value false. - ValueMetric* metric1 = config.add_value_metric(); - metric1->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialFalse")); - metric1->set_what(pulledAtomMatcher.id()); - *metric1->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - metric1->set_bucket(FIVE_MINUTES); - metric1->set_condition(screenOnFalsePredicate->id()); - - // Simple condition metric with initial value unknown. - ValueMetric* metric2 = config.add_value_metric(); - metric2->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialUnknown")); - metric2->set_what(pulledAtomMatcher.id()); - *metric2->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - metric2->set_bucket(FIVE_MINUTES); - metric2->set_condition(screenOnUnknownPredicate.id()); - - // Combination condition metric with initial values false and false. - ValueMetric* metric3 = config.add_value_metric(); - metric3->set_id(StringToId("ValueSubsystemSleepWhileScreenOnFalseDeviceUnpluggedFalse")); - metric3->set_what(pulledAtomMatcher.id()); - *metric3->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - metric3->set_bucket(FIVE_MINUTES); - metric3->set_condition(screenOnFalseOnBatteryFalsePredicate->id()); - - // Combination condition metric with initial values unknown and false. - ValueMetric* metric4 = config.add_value_metric(); - metric4->set_id(StringToId("ValueSubsystemSleepWhileScreenOnUnknownDeviceUnpluggedFalse")); - metric4->set_what(pulledAtomMatcher.id()); - *metric4->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - metric4->set_bucket(FIVE_MINUTES); - metric4->set_condition(screenOnUnknownOnBatteryFalsePredicate->id()); - - return config; -} -} // anonymous namespace - -TEST(MetricsManagerTest, TestInitialConditions) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildConfigWithDifferentPredicates(); - set<int> allTagIds; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - std::vector<sp<AnomalyTracker>> allAnomalyTrackers; - std::vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, std::vector<int>> conditionToMetricMap; - unordered_map<int, std::vector<int>> trackerToMetricMap; - unordered_map<int, std::vector<int>> trackerToConditionMap; - unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_TRUE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, - trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); - ASSERT_EQ(4u, allMetricProducers.size()); - ASSERT_EQ(5u, allConditionTrackers.size()); - - ConditionKey queryKey; - vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated); - - allConditionTrackers[3]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache); - allConditionTrackers[4]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache); - EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]); - EXPECT_EQ(ConditionState::kFalse, conditionCache[1]); - EXPECT_EQ(ConditionState::kFalse, conditionCache[2]); - EXPECT_EQ(ConditionState::kFalse, conditionCache[3]); - EXPECT_EQ(ConditionState::kUnknown, conditionCache[4]); - - EXPECT_EQ(ConditionState::kFalse, allMetricProducers[0]->mCondition); - EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[1]->mCondition); - EXPECT_EQ(ConditionState::kFalse, allMetricProducers[2]->mCondition); - EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition); -} - -TEST(MetricsManagerTest, TestGoodConfig) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildGoodConfig(); - set<int> allTagIds; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - std::vector<sp<AnomalyTracker>> allAnomalyTrackers; - std::vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, std::vector<int>> conditionToMetricMap; - unordered_map<int, std::vector<int>> trackerToMetricMap; - unordered_map<int, std::vector<int>> trackerToConditionMap; - unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_TRUE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, - trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); - ASSERT_EQ(1u, allMetricProducers.size()); - EXPECT_THAT(metricProducerMap, UnorderedElementsAre(Pair(config.count_metric(0).id(), 0))); - ASSERT_EQ(1u, allAnomalyTrackers.size()); - ASSERT_EQ(1u, noReportMetricIds.size()); - ASSERT_EQ(1u, alertTrackerMap.size()); - EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end()); - EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0); -} - -TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildDimensionMetricsWithMultiTags(); - set<int> allTagIds; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - std::vector<sp<AnomalyTracker>> allAnomalyTrackers; - std::vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, std::vector<int>> conditionToMetricMap; - unordered_map<int, std::vector<int>> trackerToMetricMap; - unordered_map<int, std::vector<int>> trackerToConditionMap; - unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, - trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); -} - -TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildCircleMatchers(); - set<int> allTagIds; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - std::vector<sp<AnomalyTracker>> allAnomalyTrackers; - std::vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, std::vector<int>> conditionToMetricMap; - unordered_map<int, std::vector<int>> trackerToMetricMap; - unordered_map<int, std::vector<int>> trackerToConditionMap; - unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, - trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); -} - -TEST(MetricsManagerTest, TestMissingMatchers) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildMissingMatchers(); - set<int> allTagIds; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - std::vector<sp<AnomalyTracker>> allAnomalyTrackers; - std::vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, std::vector<int>> conditionToMetricMap; - unordered_map<int, std::vector<int>> trackerToMetricMap; - unordered_map<int, std::vector<int>> trackerToConditionMap; - unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, - trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); -} - -TEST(MetricsManagerTest, TestMissingPredicate) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildMissingPredicate(); - set<int> allTagIds; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - std::vector<sp<AnomalyTracker>> allAnomalyTrackers; - std::vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, std::vector<int>> conditionToMetricMap; - unordered_map<int, std::vector<int>> trackerToMetricMap; - unordered_map<int, std::vector<int>> trackerToConditionMap; - unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, - trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); -} - -TEST(MetricsManagerTest, TestCirclePredicateDependency) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildCirclePredicates(); - set<int> allTagIds; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - std::vector<sp<AnomalyTracker>> allAnomalyTrackers; - std::vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, std::vector<int>> conditionToMetricMap; - unordered_map<int, std::vector<int>> trackerToMetricMap; - unordered_map<int, std::vector<int>> trackerToConditionMap; - unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, - trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); -} - -TEST(MetricsManagerTest, testAlertWithUnknownMetric) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildAlertWithUnknownMetric(); - set<int> allTagIds; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - std::vector<sp<AnomalyTracker>> allAnomalyTrackers; - std::vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, std::vector<int>> conditionToMetricMap; - unordered_map<int, std::vector<int>> trackerToMetricMap; - unordered_map<int, std::vector<int>> trackerToConditionMap; - unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, - trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); -} - -TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerInvalidMatcher) { - sp<UidMap> uidMap = new UidMap(); - AtomMatcher matcher; - // Matcher has no contents_case (simple/combination), so it is invalid. - matcher.set_id(21); - EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap), nullptr); -} - -TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple) { - int index = 1; - int64_t id = 123; - sp<UidMap> uidMap = new UidMap(); - AtomMatcher matcher; - matcher.set_id(id); - SimpleAtomMatcher* simpleAtomMatcher = matcher.mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(util::SCREEN_STATE_CHANGED); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - android::view::DisplayStateEnum::DISPLAY_STATE_ON); - - sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap); - EXPECT_NE(tracker, nullptr); - - EXPECT_TRUE(tracker->mInitialized); - EXPECT_EQ(tracker->getId(), id); - EXPECT_EQ(tracker->mIndex, index); - const set<int>& atomIds = tracker->getAtomIds(); - ASSERT_EQ(atomIds.size(), 1); - EXPECT_EQ(atomIds.count(util::SCREEN_STATE_CHANGED), 1); -} - -TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination) { - int index = 1; - int64_t id = 123; - sp<UidMap> uidMap = new UidMap(); - AtomMatcher matcher; - matcher.set_id(id); - AtomMatcher_Combination* combination = matcher.mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(123); - combination->add_matcher(223); - - sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap); - EXPECT_NE(tracker, nullptr); - - // Combination matchers need to be initialized first. - EXPECT_FALSE(tracker->mInitialized); - EXPECT_EQ(tracker->getId(), id); - EXPECT_EQ(tracker->mIndex, index); - const set<int>& atomIds = tracker->getAtomIds(); - ASSERT_EQ(atomIds.size(), 0); -} - -TEST(MetricsManagerTest, TestCreateConditionTrackerInvalid) { - const ConfigKey key(123, 456); - // Predicate has no contents_case (simple/combination), so it is invalid. - Predicate predicate; - predicate.set_id(21); - unordered_map<int64_t, int> atomTrackerMap; - EXPECT_EQ(createConditionTracker(key, predicate, 0, atomTrackerMap), nullptr); -} - -TEST(MetricsManagerTest, TestCreateConditionTrackerSimple) { - int index = 1; - int64_t id = 987; - const ConfigKey key(123, 456); - - int startMatcherIndex = 2, stopMatcherIndex = 0, stopAllMatcherIndex = 1; - int64_t startMatcherId = 246, stopMatcherId = 153, stopAllMatcherId = 975; - - Predicate predicate; - predicate.set_id(id); - SimplePredicate* simplePredicate = predicate.mutable_simple_predicate(); - simplePredicate->set_start(startMatcherId); - simplePredicate->set_stop(stopMatcherId); - simplePredicate->set_stop_all(stopAllMatcherId); - - unordered_map<int64_t, int> atomTrackerMap; - atomTrackerMap[startMatcherId] = startMatcherIndex; - atomTrackerMap[stopMatcherId] = stopMatcherIndex; - atomTrackerMap[stopAllMatcherId] = stopAllMatcherIndex; - - sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap); - EXPECT_EQ(tracker->getConditionId(), id); - EXPECT_EQ(tracker->isSliced(), false); - EXPECT_TRUE(tracker->IsSimpleCondition()); - const set<int>& interestedMatchers = tracker->getAtomMatchingTrackerIndex(); - ASSERT_EQ(interestedMatchers.size(), 3); - ASSERT_EQ(interestedMatchers.count(startMatcherIndex), 1); - ASSERT_EQ(interestedMatchers.count(stopMatcherIndex), 1); - ASSERT_EQ(interestedMatchers.count(stopAllMatcherIndex), 1); -} - -TEST(MetricsManagerTest, TestCreateConditionTrackerCombination) { - int index = 1; - int64_t id = 987; - const ConfigKey key(123, 456); - - Predicate predicate; - predicate.set_id(id); - Predicate_Combination* combinationPredicate = predicate.mutable_combination(); - combinationPredicate->set_operation(LogicalOperation::AND); - combinationPredicate->add_predicate(888); - combinationPredicate->add_predicate(777); - - // Combination conditions must be initialized to set most state. - unordered_map<int64_t, int> atomTrackerMap; - sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap); - EXPECT_EQ(tracker->getConditionId(), id); - EXPECT_FALSE(tracker->IsSimpleCondition()); -} - -TEST(MetricsManagerTest, TestCreateAnomalyTrackerInvalidMetric) { - Alert alert; - alert.set_id(123); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(1); - alert.set_num_buckets(1); - - sp<AlarmMonitor> anomalyAlarmMonitor; - vector<sp<MetricProducer>> metricProducers; - // Pass in empty metric producers, causing an error. - EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {}, metricProducers), nullopt); -} - -TEST(MetricsManagerTest, TestCreateAnomalyTrackerNoThreshold) { - int64_t metricId = 1; - Alert alert; - alert.set_id(123); - alert.set_metric_id(metricId); - alert.set_num_buckets(1); - - CountMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<sp<MetricProducer>> metricProducers({new CountMetricProducer( - kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)}); - sp<AlarmMonitor> anomalyAlarmMonitor; - EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt); -} - -TEST(MetricsManagerTest, TestCreateAnomalyTrackerMissingBuckets) { - int64_t metricId = 1; - Alert alert; - alert.set_id(123); - alert.set_metric_id(metricId); - alert.set_trigger_if_sum_gt(1); - - CountMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<sp<MetricProducer>> metricProducers({new CountMetricProducer( - kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)}); - sp<AlarmMonitor> anomalyAlarmMonitor; - EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt); -} - -TEST(MetricsManagerTest, TestCreateAnomalyTrackerGood) { - int64_t metricId = 1; - Alert alert; - alert.set_id(123); - alert.set_metric_id(metricId); - alert.set_trigger_if_sum_gt(1); - alert.set_num_buckets(1); - - CountMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<sp<MetricProducer>> metricProducers({new CountMetricProducer( - kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)}); - sp<AlarmMonitor> anomalyAlarmMonitor; - EXPECT_NE(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt); -} - -TEST(MetricsManagerTest, TestCreateAnomalyTrackerDurationTooLong) { - int64_t metricId = 1; - Alert alert; - alert.set_id(123); - alert.set_metric_id(metricId); - // Impossible for alert to fire since the time is bigger than bucketSize * numBuckets - alert.set_trigger_if_sum_gt(MillisToNano(TimeUnitToBucketSizeInMillis(ONE_MINUTE)) + 1); - alert.set_num_buckets(1); - - DurationMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_SUM); - FieldMatcher dimensions; - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<sp<MetricProducer>> metricProducers({new DurationMetricProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, 0x0123456789, dimensions, 0, 0)}); - sp<AlarmMonitor> anomalyAlarmMonitor; - EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt); -} - -} // namespace statsd -} // namespace os -} // namespace android - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp deleted file mode 100644 index 1ba8593231b0..000000000000 --- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// 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. - -#include "src/shell/ShellSubscriber.h" - -#include <gtest/gtest.h> -#include <stdio.h> -#include <unistd.h> - -#include <vector> - -#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h" -#include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h" -#include "frameworks/proto_logging/stats/atoms.pb.h" -#include "stats_event.h" -#include "tests/metrics/metrics_test_helper.h" -#include "tests/statsd_test_util.h" - -using namespace android::os::statsd; -using android::sp; -using std::vector; -using testing::_; -using testing::Invoke; -using testing::NaggyMock; -using testing::StrictMock; - -#ifdef __ANDROID__ - -void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap, - sp<MockStatsPullerManager> pullerManager, - const vector<std::shared_ptr<LogEvent>>& pushedEvents, - const ShellData& expectedData) { - // set up 2 pipes for read/write config and data - int fds_config[2]; - ASSERT_EQ(0, pipe(fds_config)); - - int fds_data[2]; - ASSERT_EQ(0, pipe(fds_data)); - - size_t bufferSize = config.ByteSize(); - // write the config to pipe, first write size of the config - write(fds_config[1], &bufferSize, sizeof(bufferSize)); - // then write config itself - vector<uint8_t> buffer(bufferSize); - config.SerializeToArray(&buffer[0], bufferSize); - write(fds_config[1], buffer.data(), bufferSize); - close(fds_config[1]); - - sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager); - - // mimic a binder thread that a shell subscriber runs on. it would block. - std::thread reader([&shellClient, &fds_config, &fds_data] { - shellClient->startNewSubscription(fds_config[0], fds_data[1], /*timeoutSec=*/-1); - }); - reader.detach(); - - // let the shell subscriber to receive the config from pipe. - std::this_thread::sleep_for(100ms); - - if (pushedEvents.size() > 0) { - // send a log event that matches the config. - std::thread log_reader([&shellClient, &pushedEvents] { - for (const auto& event : pushedEvents) { - shellClient->onLogEvent(*event); - } - }); - - log_reader.detach(); - - if (log_reader.joinable()) { - log_reader.join(); - } - } - - // wait for the data to be written. - std::this_thread::sleep_for(100ms); - - // Because we might receive heartbeats from statsd, consisting of data sizes - // of 0, encapsulate reads within a while loop. - bool readAtom = false; - while (!readAtom) { - // Read the atom size. - size_t dataSize = 0; - read(fds_data[0], &dataSize, sizeof(dataSize)); - if (dataSize == 0) continue; - EXPECT_EQ(expectedData.ByteSize(), int(dataSize)); - - // Read that much data in proto binary format. - vector<uint8_t> dataBuffer(dataSize); - EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize)); - - // Make sure the received bytes can be parsed to an atom. - ShellData receivedAtom; - EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0); - - // Serialize the expected atom to byte array and compare to make sure - // they are the same. - vector<uint8_t> expectedAtomBuffer(expectedData.ByteSize()); - expectedData.SerializeToArray(expectedAtomBuffer.data(), expectedData.ByteSize()); - EXPECT_EQ(expectedAtomBuffer, dataBuffer); - - readAtom = true; - } - - close(fds_data[0]); - if (reader.joinable()) { - reader.join(); - } -} - -TEST(ShellSubscriberTest, testPushedSubscription) { - sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - vector<std::shared_ptr<LogEvent>> pushedList; - - // Create the LogEvent from an AStatsEvent - std::unique_ptr<LogEvent> logEvent = CreateScreenStateChangedEvent( - 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON); - pushedList.push_back(std::move(logEvent)); - - // create a simple config to get screen events - ShellSubscription config; - config.add_pushed()->set_atom_id(29); - - // this is the expected screen event atom. - ShellData shellData; - shellData.add_atom()->mutable_screen_state_changed()->set_state( - ::android::view::DisplayStateEnum::DISPLAY_STATE_ON); - - runShellTest(config, uidMap, pullerManager, pushedList, shellData); -} - -namespace { - -int kUid1 = 1000; -int kUid2 = 2000; - -int kCpuTime1 = 100; -int kCpuTime2 = 200; - -ShellData getExpectedShellData() { - ShellData shellData; - auto* atom1 = shellData.add_atom()->mutable_cpu_active_time(); - atom1->set_uid(kUid1); - atom1->set_time_millis(kCpuTime1); - - auto* atom2 = shellData.add_atom()->mutable_cpu_active_time(); - atom2->set_uid(kUid2); - atom2->set_time_millis(kCpuTime2); - - return shellData; -} - -ShellSubscription getPulledConfig() { - ShellSubscription config; - auto* pull_config = config.add_pulled(); - pull_config->mutable_matcher()->set_atom_id(10016); - pull_config->set_freq_millis(2000); - return config; -} - -shared_ptr<LogEvent> makeCpuActiveTimeAtom(int32_t uid, int64_t timeMillis) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 10016); - AStatsEvent_overwriteTimestamp(statsEvent, 1111L); - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeInt64(statsEvent, timeMillis); - - std::shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -} // namespace - -TEST(ShellSubscriberTest, testPulledSubscription) { - sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); - - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - const vector<int32_t> uids = {AID_SYSTEM}; - EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _)) - .WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&, const int64_t, - vector<std::shared_ptr<LogEvent>>* data) { - data->clear(); - data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1)); - data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2)); - return true; - })); - runShellTest(getPulledConfig(), uidMap, pullerManager, vector<std::shared_ptr<LogEvent>>(), - getExpectedShellData()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp deleted file mode 100644 index 6516c1529514..000000000000 --- a/cmds/statsd/tests/state/StateTracker_test.cpp +++ /dev/null @@ -1,571 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "state/StateTracker.h" - -#include <gtest/gtest.h> -#include <private/android_filesystem_config.h> - -#include "state/StateListener.h" -#include "state/StateManager.h" -#include "state/StateTracker.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -const int32_t timestampNs = 1000; - -/** - * Mock StateListener class for testing. - * Stores primary key and state pairs. - */ -class TestStateListener : public virtual StateListener { -public: - TestStateListener(){}; - - virtual ~TestStateListener(){}; - - struct Update { - Update(const HashableDimensionKey& key, int state) : mKey(key), mState(state){}; - HashableDimensionKey mKey; - int mState; - }; - - std::vector<Update> updates; - - void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const FieldValue& oldState, - const FieldValue& newState) { - updates.emplace_back(primaryKey, newState.mValue.int_value); - } -}; - -int getStateInt(StateManager& mgr, int atomId, const HashableDimensionKey& queryKey) { - FieldValue output; - mgr.getStateValue(atomId, queryKey, &output); - return output.mValue.int_value; -} - -// START: build event functions. -// Incorrect event - missing fields -std::unique_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, - int state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, 1000); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeString(statsEvent, packageName.c_str()); - // Missing field 3 - using_alert_window. - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -// Incorrect event - exclusive state has wrong type -std::unique_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, 1000); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeString(statsEvent, packageName.c_str()); - AStatsEvent_writeInt32(statsEvent, true); // using_alert_window - AStatsEvent_writeString(statsEvent, "string"); // exclusive state: string instead of int - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} -// END: build event functions. - -TEST(StateListenerTest, TestStateListenerWeakPointer) { - sp<TestStateListener> listener = new TestStateListener(); - wp<TestStateListener> wListener = listener; - listener = nullptr; // let go of listener - EXPECT_TRUE(wListener.promote() == nullptr); -} - -TEST(StateManagerTest, TestStateManagerGetInstance) { - sp<TestStateListener> listener1 = new TestStateListener(); - StateManager& mgr = StateManager::getInstance(); - mgr.clear(); - - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); -} - -TEST(StateManagerTest, TestOnLogEvent) { - sp<MockUidMap> uidMap = makeMockUidMapForPackage("com.android.systemui", {10111}); - sp<TestStateListener> listener1 = new TestStateListener(); - StateManager mgr; - mgr.updateLogSources(uidMap); - // Add StateTracker by registering a listener. - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - - // log event using AID_ROOT - std::unique_ptr<LogEvent> event = CreateScreenStateChangedEvent( - timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - mgr.onLogEvent(*event); - - // check StateTracker was updated by querying for state - HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); - - // log event using mocked uid - event = CreateScreenStateChangedEvent( - timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_OFF, 10111); - mgr.onLogEvent(*event); - - // check StateTracker was updated by querying for state - queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); - - // log event using non-whitelisted uid - event = CreateScreenStateChangedEvent(timestampNs, - android::view::DisplayStateEnum::DISPLAY_STATE_ON, 10112); - mgr.onLogEvent(*event); - - // check StateTracker was NOT updated by querying for state - queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); - - // log event using AID_SYSTEM - event = CreateScreenStateChangedEvent( - timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON, AID_SYSTEM); - mgr.onLogEvent(*event); - - // check StateTracker was updated by querying for state - queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); -} - -/** - * Test registering listeners to StateTrackers - * - * - StateManager will create a new StateTracker if it doesn't already exist - * and then register the listener to the StateTracker. - * - If a listener is already registered to a StateTracker, it is not added again. - * - StateTrackers are only created for atoms that are state atoms. - */ -TEST(StateTrackerTest, TestRegisterListener) { - sp<TestStateListener> listener1 = new TestStateListener(); - sp<TestStateListener> listener2 = new TestStateListener(); - StateManager mgr; - - // Register listener to non-existing StateTracker - EXPECT_EQ(0, mgr.getStateTrackersCount()); - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Register listener to existing StateTracker - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Register already registered listener to existing StateTracker - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Register listener to non-state atom - mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2); - EXPECT_EQ(2, mgr.getStateTrackersCount()); -} - -/** - * Test unregistering listeners from StateTrackers - * - * - StateManager will unregister listeners from a StateTracker only if the - * StateTracker exists and the listener is registered to the StateTracker. - * - Once all listeners are removed from a StateTracker, the StateTracker - * is also removed. - */ -TEST(StateTrackerTest, TestUnregisterListener) { - sp<TestStateListener> listener1 = new TestStateListener(); - sp<TestStateListener> listener2 = new TestStateListener(); - StateManager mgr; - - // Unregister listener from non-existing StateTracker - EXPECT_EQ(0, mgr.getStateTrackersCount()); - mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener1); - EXPECT_EQ(0, mgr.getStateTrackersCount()); - EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Unregister non-registered listener from existing StateTracker - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener2); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Unregister second-to-last listener from StateTracker - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2); - mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener1); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Unregister last listener from StateTracker - mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener2); - EXPECT_EQ(0, mgr.getStateTrackersCount()); - EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); -} - -/** - * Test a binary state atom with nested counting. - * - * To go from an "ON" state to an "OFF" state with nested counting, we must see - * an equal number of "OFF" events as "ON" events. - * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state. - * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state. - */ -TEST(StateTrackerTest, TestStateChangeNested) { - sp<TestStateListener> listener = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener); - - std::vector<int> attributionUids1 = {1000}; - std::vector<string> attributionTags1 = {"tag"}; - - std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent(timestampNs, attributionUids1, - attributionTags1, "wakelockName"); - mgr.onLogEvent(*event1); - ASSERT_EQ(1, listener->updates.size()); - EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(1, listener->updates[0].mState); - listener->updates.clear(); - - std::unique_ptr<LogEvent> event2 = CreateAcquireWakelockEvent( - timestampNs + 1000, attributionUids1, attributionTags1, "wakelockName"); - mgr.onLogEvent(*event2); - ASSERT_EQ(0, listener->updates.size()); - - std::unique_ptr<LogEvent> event3 = CreateReleaseWakelockEvent( - timestampNs + 2000, attributionUids1, attributionTags1, "wakelockName"); - mgr.onLogEvent(*event3); - ASSERT_EQ(0, listener->updates.size()); - - std::unique_ptr<LogEvent> event4 = CreateReleaseWakelockEvent( - timestampNs + 3000, attributionUids1, attributionTags1, "wakelockName"); - mgr.onLogEvent(*event4); - ASSERT_EQ(1, listener->updates.size()); - EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(0, listener->updates[0].mState); -} - -/** - * Test a state atom with a reset state. - * - * If the reset state value is seen, every state in the map is set to the default - * state and every listener is notified. - */ -TEST(StateTrackerTest, TestStateChangeReset) { - sp<TestStateListener> listener = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::BLE_SCAN_STATE_CHANGED, listener); - - std::vector<int> attributionUids1 = {1000}; - std::vector<string> attributionTags1 = {"tag1"}; - std::vector<int> attributionUids2 = {2000}; - - std::unique_ptr<LogEvent> event1 = - CreateBleScanStateChangedEvent(timestampNs, attributionUids1, attributionTags1, - BleScanStateChanged::ON, false, false, false); - mgr.onLogEvent(*event1); - ASSERT_EQ(1, listener->updates.size()); - EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState); - FieldValue stateFieldValue; - mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue); - EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value); - listener->updates.clear(); - - std::unique_ptr<LogEvent> event2 = - CreateBleScanStateChangedEvent(timestampNs + 1000, attributionUids2, attributionTags1, - BleScanStateChanged::ON, false, false, false); - mgr.onLogEvent(*event2); - ASSERT_EQ(1, listener->updates.size()); - EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState); - mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue); - EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value); - listener->updates.clear(); - - std::unique_ptr<LogEvent> event3 = - CreateBleScanStateChangedEvent(timestampNs + 2000, attributionUids2, attributionTags1, - BleScanStateChanged::RESET, false, false, false); - mgr.onLogEvent(*event3); - ASSERT_EQ(2, listener->updates.size()); - for (const TestStateListener::Update& update : listener->updates) { - EXPECT_EQ(BleScanStateChanged::OFF, update.mState); - - mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, update.mKey, &stateFieldValue); - EXPECT_EQ(BleScanStateChanged::OFF, stateFieldValue.mValue.int_value); - } -} - -/** - * Test StateManager's onLogEvent and StateListener's onStateChanged correctly - * updates listener for states without primary keys. - */ -TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) { - sp<TestStateListener> listener1 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - - // log event - std::unique_ptr<LogEvent> event = CreateScreenStateChangedEvent( - timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - mgr.onLogEvent(*event); - - // check listener was updated - ASSERT_EQ(1, listener1->updates.size()); - EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey); - EXPECT_EQ(2, listener1->updates[0].mState); - - // check StateTracker was updated by querying for state - HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); -} - -/** - * Test StateManager's onLogEvent and StateListener's onStateChanged correctly - * updates listener for states with one primary key. - */ -TEST(StateTrackerTest, TestStateChangeOnePrimaryField) { - sp<TestStateListener> listener1 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener1); - - // log event - std::unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent( - timestampNs, 1000 /*uid*/, android::app::ProcessStateEnum::PROCESS_STATE_TOP); - mgr.onLogEvent(*event); - - // check listener was updated - ASSERT_EQ(1, listener1->updates.size()); - EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(1002, listener1->updates[0].mState); - - // check StateTracker was updated by querying for state - HashableDimensionKey queryKey; - getUidProcessKey(1000 /* uid */, &queryKey); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP, - getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey)); -} - -TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) { - sp<TestStateListener> listener1 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener1); - - // Log event. - std::vector<int> attributionUids = {1001}; - std::vector<string> attributionTags = {"tag1"}; - - std::unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timestampNs, attributionUids, - attributionTags, "wakelockName"); - mgr.onLogEvent(*event); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, mgr.getListenersCount(util::WAKELOCK_STATE_CHANGED)); - - // Check listener was updated. - ASSERT_EQ(1, listener1->updates.size()); - ASSERT_EQ(3, listener1->updates[0].mKey.getValues().size()); - EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value); - EXPECT_EQ("wakelockName", listener1->updates[0].mKey.getValues()[2].mValue.str_value); - EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState); - - // Check StateTracker was updated by querying for state. - HashableDimensionKey queryKey; - getPartialWakelockKey(1001 /* uid */, "wakelockName", &queryKey); - EXPECT_EQ(WakelockStateChanged::ACQUIRE, - getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey)); - - // No state stored for this query key. - HashableDimensionKey queryKey2; - getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2); - EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, - getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2)); - - // Partial query fails. - HashableDimensionKey queryKey3; - getPartialWakelockKey(1001 /* uid */, &queryKey3); - EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, - getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3)); -} - -/** - * Test StateManager's onLogEvent and StateListener's onStateChanged correctly - * updates listener for states with multiple primary keys. - */ -TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) { - sp<TestStateListener> listener1 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1); - - // log event - std::unique_ptr<LogEvent> event = CreateOverlayStateChangedEvent( - timestampNs, 1000 /* uid */, "package1", true /*using_alert_window*/, - OverlayStateChanged::ENTERED); - mgr.onLogEvent(*event); - - // check listener was updated - ASSERT_EQ(1, listener1->updates.size()); - EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(1, listener1->updates[0].mState); - - // check StateTracker was updated by querying for state - HashableDimensionKey queryKey; - getOverlayKey(1000 /* uid */, "package1", &queryKey); - EXPECT_EQ(OverlayStateChanged::ENTERED, - getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey)); -} - -/** - * Test StateManager's onLogEvent and StateListener's onStateChanged - * when there is an error extracting state from log event. Listener is not - * updated of state change. - */ -TEST(StateTrackerTest, TestStateChangeEventError) { - sp<TestStateListener> listener1 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1); - - // log event - std::shared_ptr<LogEvent> event1 = - buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */); - std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2"); - - // check listener was updated - mgr.onLogEvent(*event1); - ASSERT_EQ(0, listener1->updates.size()); - mgr.onLogEvent(*event2); - ASSERT_EQ(0, listener1->updates.size()); -} - -TEST(StateTrackerTest, TestStateQuery) { - sp<TestStateListener> listener1 = new TestStateListener(); - sp<TestStateListener> listener2 = new TestStateListener(); - sp<TestStateListener> listener3 = new TestStateListener(); - sp<TestStateListener> listener4 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener2); - mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener3); - mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener4); - - std::unique_ptr<LogEvent> event1 = CreateUidProcessStateChangedEvent( - timestampNs, 1000 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 - std::unique_ptr<LogEvent> event2 = CreateUidProcessStateChangedEvent( - timestampNs + 1000, 1001 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: - // 1003 - std::unique_ptr<LogEvent> event3 = CreateUidProcessStateChangedEvent( - timestampNs + 2000, 1002 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000 - std::unique_ptr<LogEvent> event4 = CreateUidProcessStateChangedEvent( - timestampNs + 3000, 1001 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 - std::unique_ptr<LogEvent> event5 = CreateScreenStateChangedEvent( - timestampNs + 4000, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - std::unique_ptr<LogEvent> event6 = CreateOverlayStateChangedEvent( - timestampNs + 5000, 1000 /*uid*/, "package1", true /*using_alert_window*/, - OverlayStateChanged::ENTERED); - std::unique_ptr<LogEvent> event7 = CreateOverlayStateChangedEvent( - timestampNs + 6000, 1000 /*uid*/, "package2", true /*using_alert_window*/, - OverlayStateChanged::EXITED); - - std::vector<int> attributionUids = {1005}; - std::vector<string> attributionTags = {"tag"}; - - std::unique_ptr<LogEvent> event8 = CreateAcquireWakelockEvent( - timestampNs + 7000, attributionUids, attributionTags, "wakelock1"); - std::unique_ptr<LogEvent> event9 = CreateReleaseWakelockEvent( - timestampNs + 8000, attributionUids, attributionTags, "wakelock2"); - - mgr.onLogEvent(*event1); - mgr.onLogEvent(*event2); - mgr.onLogEvent(*event3); - mgr.onLogEvent(*event5); - mgr.onLogEvent(*event5); - mgr.onLogEvent(*event6); - mgr.onLogEvent(*event7); - mgr.onLogEvent(*event8); - mgr.onLogEvent(*event9); - - // Query for UidProcessState of uid 1001 - HashableDimensionKey queryKey1; - getUidProcessKey(1001, &queryKey1); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, - getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1)); - - // Query for UidProcessState of uid 1004 - not in state map - HashableDimensionKey queryKey2; - getUidProcessKey(1004, &queryKey2); - EXPECT_EQ(-1, getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, - queryKey2)); // default state - - // Query for UidProcessState of uid 1001 - after change in state - mgr.onLogEvent(*event4); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP, - getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1)); - - // Query for ScreenState - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY)); - - // Query for OverlayState of uid 1000, package name "package2" - HashableDimensionKey queryKey3; - getOverlayKey(1000, "package2", &queryKey3); - EXPECT_EQ(OverlayStateChanged::EXITED, - getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey3)); - - // Query for WakelockState of uid 1005, tag 2 - HashableDimensionKey queryKey4; - getPartialWakelockKey(1005, "wakelock2", &queryKey4); - EXPECT_EQ(WakelockStateChanged::RELEASE, - getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey4)); - - // Query for WakelockState of uid 1005, tag 1 - HashableDimensionKey queryKey5; - getPartialWakelockKey(1005, "wakelock1", &queryKey5); - EXPECT_EQ(WakelockStateChanged::ACQUIRE, - getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5)); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp deleted file mode 100644 index 1761d5d9e1fa..000000000000 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ /dev/null @@ -1,1409 +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. - -#include "statsd_test_util.h" - -#include <aidl/android/util/StatsEventParcel.h> - -#include "matchers/SimpleAtomMatchingTracker.h" -#include "stats_event.h" - -using aidl::android::util::StatsEventParcel; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -StatsLogReport outputStreamToProto(ProtoOutputStream* proto) { - vector<uint8_t> bytes; - bytes.resize(proto->size()); - size_t pos = 0; - sp<ProtoReader> reader = proto->data(); - - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((bytes)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - StatsLogReport report; - report.ParseFromArray(bytes.data(), bytes.size()); - return report; -} - -AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(atomId); - return atom_matcher; -} - -AtomMatcher CreateTemperatureAtomMatcher() { - return CreateSimpleAtomMatcher("TemperatureMatcher", util::TEMPERATURE); -} - -AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name, - ScheduledJobStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::SCHEDULED_JOB_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(3); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateStartScheduledJobAtomMatcher() { - return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart", - ScheduledJobStateChanged::STARTED); -} - -AtomMatcher CreateFinishScheduledJobAtomMatcher() { - return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish", - ScheduledJobStateChanged::FINISHED); -} - -AtomMatcher CreateScreenBrightnessChangedAtomMatcher() { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId("ScreenBrightnessChanged")); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::SCREEN_BRIGHTNESS_CHANGED); - return atom_matcher; -} - -AtomMatcher CreateUidProcessStateChangedAtomMatcher() { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId("UidProcessStateChanged")); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::UID_PROCESS_STATE_CHANGED); - return atom_matcher; -} - -AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name, - WakelockStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::WAKELOCK_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(4); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateAcquireWakelockAtomMatcher() { - return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE); -} - -AtomMatcher CreateReleaseWakelockAtomMatcher() { - return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE); -} - -AtomMatcher CreateBatterySaverModeStateChangedAtomMatcher( - const string& name, BatterySaverModeStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(1); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateBatterySaverModeStartAtomMatcher() { - return CreateBatterySaverModeStateChangedAtomMatcher( - "BatterySaverModeStart", BatterySaverModeStateChanged::ON); -} - - -AtomMatcher CreateBatterySaverModeStopAtomMatcher() { - return CreateBatterySaverModeStateChangedAtomMatcher( - "BatterySaverModeStop", BatterySaverModeStateChanged::OFF); -} - -AtomMatcher CreateBatteryStateChangedAtomMatcher(const string& name, - BatteryPluggedStateEnum state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::PLUGGED_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(1); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateBatteryStateNoneMatcher() { - return CreateBatteryStateChangedAtomMatcher("BatteryPluggedNone", - BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE); -} - -AtomMatcher CreateBatteryStateUsbMatcher() { - return CreateBatteryStateChangedAtomMatcher("BatteryPluggedUsb", - BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); -} - -AtomMatcher CreateScreenStateChangedAtomMatcher( - const string& name, android::view::DisplayStateEnum state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::SCREEN_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(1); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateScreenTurnedOnAtomMatcher() { - return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", - android::view::DisplayStateEnum::DISPLAY_STATE_ON); -} - -AtomMatcher CreateScreenTurnedOffAtomMatcher() { - return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff", - ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF); -} - -AtomMatcher CreateSyncStateChangedAtomMatcher( - const string& name, SyncStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::SYNC_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(3); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateSyncStartAtomMatcher() { - return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON); -} - -AtomMatcher CreateSyncEndAtomMatcher() { - return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF); -} - -AtomMatcher CreateActivityForegroundStateChangedAtomMatcher( - const string& name, ActivityForegroundStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::ACTIVITY_FOREGROUND_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(4); // Activity field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateMoveToBackgroundAtomMatcher() { - return CreateActivityForegroundStateChangedAtomMatcher( - "Background", ActivityForegroundStateChanged::BACKGROUND); -} - -AtomMatcher CreateMoveToForegroundAtomMatcher() { - return CreateActivityForegroundStateChangedAtomMatcher( - "Foreground", ActivityForegroundStateChanged::FOREGROUND); -} - -AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher( - const string& name, ProcessLifeCycleStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(3); // Process state field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateProcessCrashAtomMatcher() { - return CreateProcessLifeCycleStateChangedAtomMatcher( - "Crashed", ProcessLifeCycleStateChanged::CRASHED); -} - -Predicate CreateScheduledJobPredicate() { - Predicate predicate; - predicate.set_id(StringToId("ScheduledJobRunningPredicate")); - predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish")); - return predicate; -} - -Predicate CreateBatterySaverModePredicate() { - Predicate predicate; - predicate.set_id(StringToId("BatterySaverIsOn")); - predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop")); - return predicate; -} - -Predicate CreateDeviceUnpluggedPredicate() { - Predicate predicate; - predicate.set_id(StringToId("DeviceUnplugged")); - predicate.mutable_simple_predicate()->set_start(StringToId("BatteryPluggedNone")); - predicate.mutable_simple_predicate()->set_stop(StringToId("BatteryPluggedUsb")); - return predicate; -} - -Predicate CreateScreenIsOnPredicate() { - Predicate predicate; - predicate.set_id(StringToId("ScreenIsOn")); - predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff")); - return predicate; -} - -Predicate CreateScreenIsOffPredicate() { - Predicate predicate; - predicate.set_id(1111123); - predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn")); - return predicate; -} - -Predicate CreateHoldingWakelockPredicate() { - Predicate predicate; - predicate.set_id(StringToId("HoldingWakelock")); - predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock")); - return predicate; -} - -Predicate CreateIsSyncingPredicate() { - Predicate predicate; - predicate.set_id(33333333333333); - predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd")); - return predicate; -} - -Predicate CreateIsInBackgroundPredicate() { - Predicate predicate; - predicate.set_id(StringToId("IsInBackground")); - predicate.mutable_simple_predicate()->set_start(StringToId("Background")); - predicate.mutable_simple_predicate()->set_stop(StringToId("Foreground")); - return predicate; -} - -State CreateScreenState() { - State state; - state.set_id(StringToId("ScreenState")); - state.set_atom_id(util::SCREEN_STATE_CHANGED); - return state; -} - -State CreateUidProcessState() { - State state; - state.set_id(StringToId("UidProcessState")); - state.set_atom_id(util::UID_PROCESS_STATE_CHANGED); - return state; -} - -State CreateOverlayState() { - State state; - state.set_id(StringToId("OverlayState")); - state.set_atom_id(util::OVERLAY_STATE_CHANGED); - return state; -} - -State CreateScreenStateWithOnOffMap(int64_t screenOnId, int64_t screenOffId) { - State state; - state.set_id(StringToId("ScreenStateOnOff")); - state.set_atom_id(util::SCREEN_STATE_CHANGED); - - auto map = CreateScreenStateOnOffMap(screenOnId, screenOffId); - *state.mutable_map() = map; - - return state; -} - -State CreateScreenStateWithSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId) { - State state; - state.set_id(StringToId("ScreenStateSimpleOnOff")); - state.set_atom_id(util::SCREEN_STATE_CHANGED); - - auto map = CreateScreenStateSimpleOnOffMap(screenOnId, screenOffId); - *state.mutable_map() = map; - - return state; -} - -StateMap_StateGroup CreateScreenStateOnGroup(int64_t screenOnId) { - StateMap_StateGroup group; - group.set_group_id(screenOnId); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_VR); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND); - return group; -} - -StateMap_StateGroup CreateScreenStateOffGroup(int64_t screenOffId) { - StateMap_StateGroup group; - group.set_group_id(screenOffId); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND); - return group; -} - -StateMap_StateGroup CreateScreenStateSimpleOnGroup(int64_t screenOnId) { - StateMap_StateGroup group; - group.set_group_id(screenOnId); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON); - return group; -} - -StateMap_StateGroup CreateScreenStateSimpleOffGroup(int64_t screenOffId) { - StateMap_StateGroup group; - group.set_group_id(screenOffId); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - return group; -} - -StateMap CreateScreenStateOnOffMap(int64_t screenOnId, int64_t screenOffId) { - StateMap map; - *map.add_group() = CreateScreenStateOnGroup(screenOnId); - *map.add_group() = CreateScreenStateOffGroup(screenOffId); - return map; -} - -StateMap CreateScreenStateSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId) { - StateMap map; - *map.add_group() = CreateScreenStateSimpleOnGroup(screenOnId); - *map.add_group() = CreateScreenStateSimpleOffGroup(screenOffId); - return map; -} - -void addPredicateToPredicateCombination(const Predicate& predicate, - Predicate* combinationPredicate) { - combinationPredicate->mutable_combination()->add_predicate(predicate.id()); -} - -FieldMatcher CreateAttributionUidDimensions(const int atomId, - const std::vector<Position>& positions) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const auto position : positions) { - auto child = dimensions.add_child(); - child->set_field(1); - child->set_position(position); - child->add_child()->set_field(1); - } - return dimensions; -} - -FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, - const std::vector<Position>& positions) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const auto position : positions) { - auto child = dimensions.add_child(); - child->set_field(1); - child->set_position(position); - child->add_child()->set_field(1); - child->add_child()->set_field(2); - } - return dimensions; -} - -FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const int field : fields) { - dimensions.add_child()->set_field(field); - } - return dimensions; -} - -FieldMatcher CreateAttributionUidAndOtherDimensions(const int atomId, - const std::vector<Position>& positions, - const std::vector<int>& fields) { - FieldMatcher dimensions = CreateAttributionUidDimensions(atomId, positions); - - for (const int field : fields) { - dimensions.add_child()->set_field(field); - } - return dimensions; -} - -// START: get primary key functions -void getUidProcessKey(int uid, HashableDimensionKey* key) { - int pos1[] = {1, 0, 0}; - Field field1(27 /* atom id */, pos1, 0 /* depth */); - Value value1((int32_t)uid); - - key->addValue(FieldValue(field1, value1)); -} - -void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) { - int pos1[] = {1, 0, 0}; - int pos2[] = {2, 0, 0}; - - Field field1(59 /* atom id */, pos1, 0 /* depth */); - Field field2(59 /* atom id */, pos2, 0 /* depth */); - - Value value1((int32_t)uid); - Value value2(packageName); - - key->addValue(FieldValue(field1, value1)); - key->addValue(FieldValue(field2, value2)); -} - -void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key) { - int pos1[] = {1, 1, 1}; - int pos3[] = {2, 0, 0}; - int pos4[] = {3, 0, 0}; - - Field field1(10 /* atom id */, pos1, 2 /* depth */); - - Field field3(10 /* atom id */, pos3, 0 /* depth */); - Field field4(10 /* atom id */, pos4, 0 /* depth */); - - Value value1((int32_t)uid); - Value value3((int32_t)1 /*partial*/); - Value value4(tag); - - key->addValue(FieldValue(field1, value1)); - key->addValue(FieldValue(field3, value3)); - key->addValue(FieldValue(field4, value4)); -} - -void getPartialWakelockKey(int uid, HashableDimensionKey* key) { - int pos1[] = {1, 1, 1}; - int pos3[] = {2, 0, 0}; - - Field field1(10 /* atom id */, pos1, 2 /* depth */); - Field field3(10 /* atom id */, pos3, 0 /* depth */); - - Value value1((int32_t)uid); - Value value3((int32_t)1 /*partial*/); - - key->addValue(FieldValue(field1, value1)); - key->addValue(FieldValue(field3, value3)); -} -// END: get primary key functions - -void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids, - const vector<string>& attributionTags) { - 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()); -} - -void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) { - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - - AStatsEvent_release(statsEvent); -} - -void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - AStatsEvent_writeInt32(statsEvent, value1); - AStatsEvent_writeInt32(statsEvent, value2); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2) { - shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2); - return logEvent; -} - -void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2, int32_t value3) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - AStatsEvent_writeInt32(statsEvent, value1); - AStatsEvent_writeInt32(statsEvent, value2); - AStatsEvent_writeInt32(statsEvent, value3); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2, int32_t value3) { - shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - CreateThreeValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2, value3); - return logEvent; -} - -void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, - int32_t value) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - AStatsEvent_writeInt32(statsEvent, value); - AStatsEvent_writeInt32(statsEvent, value); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) { - shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(logEvent.get(), atomId, eventTimeNs, value); - return logEvent; -} - -void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) { - shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(logEvent.get(), atomId, eventTimeNs); - return logEvent; -} - -shared_ptr<LogEvent> makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1, - int data2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true); - AStatsEvent_writeInt32(statsEvent, data1); - AStatsEvent_writeInt32(statsEvent, data2); - - shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -shared_ptr<LogEvent> makeAttributionLogEvent(int atomId, int64_t eventTimeNs, - const vector<int>& uids, const vector<string>& tags, - int data1, int data2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - writeAttribution(statsEvent, uids, tags); - AStatsEvent_writeInt32(statsEvent, data1); - AStatsEvent_writeInt32(statsEvent, data2); - - shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -sp<MockUidMap> makeMockUidMapForOneHost(int hostUid, const vector<int>& isolatedUids) { - sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); - EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(ReturnArg<0>()); - for (const int isolatedUid : isolatedUids) { - EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); - } - - return uidMap; -} - -sp<MockUidMap> makeMockUidMapForPackage(const string& pkg, const set<int32_t>& uids) { - sp<MockUidMap> uidMap = new StrictMock<MockUidMap>(); - EXPECT_CALL(*uidMap, getAppUid(_)).Times(AnyNumber()); - EXPECT_CALL(*uidMap, getAppUid(pkg)).WillRepeatedly(Return(uids)); - - return uidMap; -} - -std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(uint64_t timestampNs, - const android::view::DisplayStateEnum state, - int loggerUid) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(loggerUid, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::PLUGGED_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SCREEN_BRIGHTNESS_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, level); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent( - const vector<int>& attributionUids, const vector<string>& attributionTags, - const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, jobName.c_str()); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& jobName) { - return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, - ScheduledJobStateChanged::STARTED, timestampNs); -} - -// Create log event when scheduled job finishes. -std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& jobName) { - return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, - ScheduledJobStateChanged::FINISHED, timestampNs); -} - -std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& wakelockName, - const WakelockStateChanged::State state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::WAKELOCK_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true); - AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeString(statsEvent, wakelockName.c_str()); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - 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, 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); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - 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, util::SYNC_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, name.c_str()); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - 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, util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeString(statsEvent, ""); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - 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, util::APP_CRASH_OCCURRED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeString(statsEvent, "eventType"); - AStatsEvent_writeString(statsEvent, "processName"); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid, - int isolatedUid, bool is_create) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::ISOLATED_UID_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, hostUid); - AStatsEvent_writeInt32(statsEvent, isolatedUid); - AStatsEvent_writeInt32(statsEvent, is_create); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( - uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::UID_PROCESS_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const BleScanStateChanged::State state, - const bool filtered, const bool firstMatch, - const bool opportunistic) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true); - if (state == util::BLE_SCAN_STATE_CHANGED__STATE__RESET) { - AStatsEvent_addInt32Annotation(statsEvent, ANNOTATION_ID_TRIGGER_STATE_RESET, - util::BLE_SCAN_STATE_CHANGED__STATE__OFF); - } - AStatsEvent_writeBool(statsEvent, filtered); // filtered - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeBool(statsEvent, firstMatch); // first match - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeBool(statsEvent, opportunistic); // opportunistic - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid, - const string& packageName, - const bool usingAlertWindow, - const OverlayStateChanged::State state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeString(statsEvent, packageName.c_str()); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeBool(statsEvent, usingAlertWindow); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs, - const StatsdConfig& config, const ConfigKey& key, - const shared_ptr<IPullAtomCallback>& puller, - const int32_t atomTag, const sp<UidMap> uidMap) { - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - if (puller != nullptr) { - pullerManager->RegisterPullAtomCallback(/*uid=*/0, atomTag, NS_PER_SEC, NS_PER_SEC * 10, {}, - puller); - } - sp<AlarmMonitor> anomalyAlarmMonitor = - new AlarmMonitor(1, - [](const shared_ptr<IStatsCompanionService>&, int64_t){}, - [](const shared_ptr<IStatsCompanionService>&){}); - sp<AlarmMonitor> periodicAlarmMonitor = - new AlarmMonitor(1, - [](const shared_ptr<IStatsCompanionService>&, int64_t){}, - [](const shared_ptr<IStatsCompanionService>&){}); - sp<StatsLogProcessor> processor = - new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, [](const ConfigKey&) { return true; }, - [](const int&, const vector<int64_t>&) {return true;}); - processor->OnConfigUpdated(currentTimeNs, key, config); - return processor; -} - -void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) { - std::sort(events->begin(), events->end(), - [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) { - return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs(); - }); -} - -int64_t StringToId(const string& str) { - return static_cast<int64_t>(std::hash<std::string>()(str)); -} - -sp<EventMatcherWizard> createEventMatcherWizard( - int tagId, int matcherIndex, const vector<FieldValueMatcher>& fieldValueMatchers) { - sp<UidMap> uidMap = new UidMap(); - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - for (const FieldValueMatcher& fvm : fieldValueMatchers) { - *atomMatcher.add_field_value_matcher() = fvm; - } - uint64_t matcherHash = 0x12345678; - int64_t matcherId = 678; - return new EventMatcherWizard({new SimpleAtomMatchingTracker( - matcherId, matcherIndex, matcherHash, atomMatcher, uidMap)}); -} - -void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId, - const int uid, const string& tag) { - EXPECT_EQ(value.field(), atomId); - ASSERT_EQ(value.value_tuple().dimensions_value_size(), 2); - // Attribution field. - EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1); - // Uid field. - ASSERT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), - uid); - // Tag field. - EXPECT_EQ(value.value_tuple().dimensions_value(1).field(), 3); - EXPECT_EQ(value.value_tuple().dimensions_value(1).value_str(), tag); -} - -void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) { - EXPECT_EQ(value.field(), atomId); - ASSERT_EQ(value.value_tuple().dimensions_value_size(), 1); - // Attribution field. - EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1); - // Uid only. - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(0).value_int(), uid); -} - -void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid) { - EXPECT_EQ(value.field(), atomId); - ASSERT_GT(value.value_tuple().dimensions_value_size(), node_idx); - // Attribution field. - EXPECT_EQ(value.value_tuple().dimensions_value(node_idx).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(0).value_int(), uid); -} - -void ValidateAttributionUidAndTagDimension( - const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag) { - EXPECT_EQ(value.field(), atomId); - ASSERT_GT(value.value_tuple().dimensions_value_size(), node_idx); - // Attribution field. - EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx).field()); - // Uid only. - EXPECT_EQ(2, value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value_size()); - EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(0).field()); - EXPECT_EQ(uid, value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(0).value_int()); - EXPECT_EQ(2, value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(1).field()); - EXPECT_EQ(tag, value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(1).value_str()); -} - -void ValidateAttributionUidAndTagDimension( - const DimensionsValue& value, int atomId, int uid, const std::string& tag) { - EXPECT_EQ(value.field(), atomId); - ASSERT_EQ(1, value.value_tuple().dimensions_value_size()); - // Attribution field. - EXPECT_EQ(1, value.value_tuple().dimensions_value(0).field()); - // Uid only. - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value_size(), 2); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(0).value_int(), uid); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(1).field(), 2); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(1).value_str(), tag); -} - -bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) { - if (s1.field() != s2.field()) { - return false; - } - if (s1.value_case() != s2.value_case()) { - return false; - } - switch (s1.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return (s1.value_str() == s2.value_str()); - case DimensionsValue::ValueCase::kValueInt: - return s1.value_int() == s2.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return s1.value_long() == s2.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return s1.value_bool() == s2.value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return s1.value_float() == s2.value_float(); - case DimensionsValue::ValueCase::kValueTuple: { - if (s1.value_tuple().dimensions_value_size() != - s2.value_tuple().dimensions_value_size()) { - return false; - } - bool allMatched = true; - for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) { - allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i)); - } - return allMatched; - } - case DimensionsValue::ValueCase::VALUE_NOT_SET: - default: - return true; - } -} - -bool LessThan(const google::protobuf::RepeatedPtrField<StateValue>& s1, - const google::protobuf::RepeatedPtrField<StateValue>& s2) { - if (s1.size() != s2.size()) { - return s1.size() < s2.size(); - } - for (int i = 0; i < s1.size(); i++) { - const StateValue& state1 = s1[i]; - const StateValue& state2 = s2[i]; - if (state1.atom_id() != state2.atom_id()) { - return state1.atom_id() < state2.atom_id(); - } - if (state1.value() != state2.value()) { - return state1.value() < state2.value(); - } - if (state1.group_id() != state2.group_id()) { - return state1.group_id() < state2.group_id(); - } - } - return false; -} - -bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) { - if (s1.field() != s2.field()) { - return s1.field() < s2.field(); - } - if (s1.value_case() != s2.value_case()) { - return s1.value_case() < s2.value_case(); - } - switch (s1.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return s1.value_str() < s2.value_str(); - case DimensionsValue::ValueCase::kValueInt: - return s1.value_int() < s2.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return s1.value_long() < s2.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return (int)s1.value_bool() < (int)s2.value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return s1.value_float() < s2.value_float(); - case DimensionsValue::ValueCase::kValueTuple: { - if (s1.value_tuple().dimensions_value_size() != - s2.value_tuple().dimensions_value_size()) { - return s1.value_tuple().dimensions_value_size() < - s2.value_tuple().dimensions_value_size(); - } - for (int i = 0; i < s1.value_tuple().dimensions_value_size(); ++i) { - if (EqualsTo(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i))) { - continue; - } else { - return LessThan(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i)); - } - } - return false; - } - case DimensionsValue::ValueCase::VALUE_NOT_SET: - default: - return false; - } -} - -bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2) { - if (LessThan(s1.dimInWhat, s2.dimInWhat)) { - return true; - } else if (LessThan(s2.dimInWhat, s1.dimInWhat)) { - return false; - } - - return LessThan(s1.stateValues, s2.stateValues); -} - -void backfillStringInDimension(const std::map<uint64_t, string>& str_map, - DimensionsValue* dimension) { - if (dimension->has_value_str_hash()) { - auto it = str_map.find((uint64_t)(dimension->value_str_hash())); - if (it != str_map.end()) { - dimension->clear_value_str_hash(); - dimension->set_value_str(it->second); - } else { - ALOGE("Can not find the string hash: %llu", - (unsigned long long)dimension->value_str_hash()); - } - } else if (dimension->has_value_tuple()) { - auto value_tuple = dimension->mutable_value_tuple(); - for (int i = 0; i < value_tuple->dimensions_value_size(); ++i) { - backfillStringInDimension(str_map, value_tuple->mutable_dimensions_value(i)); - } - } -} - -void backfillStringInReport(ConfigMetricsReport *config_report) { - std::map<uint64_t, string> str_map; - for (const auto& str : config_report->strings()) { - uint64_t hash = Hash64(str); - if (str_map.find(hash) != str_map.end()) { - ALOGE("String hash conflicts: %s %s", str.c_str(), str_map[hash].c_str()); - } - str_map[hash] = str; - } - for (int i = 0; i < config_report->metrics_size(); ++i) { - auto metric_report = config_report->mutable_metrics(i); - if (metric_report->has_count_metrics()) { - backfillStringInDimension(str_map, metric_report->mutable_count_metrics()); - } else if (metric_report->has_duration_metrics()) { - backfillStringInDimension(str_map, metric_report->mutable_duration_metrics()); - } else if (metric_report->has_gauge_metrics()) { - backfillStringInDimension(str_map, metric_report->mutable_gauge_metrics()); - } else if (metric_report->has_value_metrics()) { - backfillStringInDimension(str_map, metric_report->mutable_value_metrics()); - } - } - // Backfill the package names. - for (int i = 0 ; i < config_report->uid_map().snapshots_size(); ++i) { - auto snapshot = config_report->mutable_uid_map()->mutable_snapshots(i); - for (int j = 0 ; j < snapshot->package_info_size(); ++j) { - auto package_info = snapshot->mutable_package_info(j); - if (package_info->has_name_hash()) { - auto it = str_map.find((uint64_t)(package_info->name_hash())); - if (it != str_map.end()) { - package_info->clear_name_hash(); - package_info->set_name(it->second); - } else { - ALOGE("Can not find the string package name hash: %llu", - (unsigned long long)package_info->name_hash()); - } - - } - } - } - // Backfill the app name in app changes. - for (int i = 0 ; i < config_report->uid_map().changes_size(); ++i) { - auto change = config_report->mutable_uid_map()->mutable_changes(i); - if (change->has_app_hash()) { - auto it = str_map.find((uint64_t)(change->app_hash())); - if (it != str_map.end()) { - change->clear_app_hash(); - change->set_app(it->second); - } else { - ALOGE("Can not find the string change app name hash: %llu", - (unsigned long long)change->app_hash()); - } - } - } -} - -void backfillStringInReport(ConfigMetricsReportList *config_report_list) { - for (int i = 0; i < config_report_list->reports_size(); ++i) { - backfillStringInReport(config_report_list->mutable_reports(i)); - } -} - -bool backfillDimensionPath(const DimensionsValue& path, - const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues, - int* leafIndex, - DimensionsValue* dimension) { - dimension->set_field(path.field()); - if (path.has_value_tuple()) { - for (int i = 0; i < path.value_tuple().dimensions_value_size(); ++i) { - if (!backfillDimensionPath( - path.value_tuple().dimensions_value(i), leafValues, leafIndex, - dimension->mutable_value_tuple()->add_dimensions_value())) { - return false; - } - } - } else { - if (*leafIndex < 0 || *leafIndex >= leafValues.size()) { - return false; - } - dimension->MergeFrom(leafValues.Get(*leafIndex)); - (*leafIndex)++; - } - return true; -} - -bool backfillDimensionPath(const DimensionsValue& path, - const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues, - DimensionsValue* dimension) { - int leafIndex = 0; - return backfillDimensionPath(path, leafValues, &leafIndex, dimension); -} - -void backfillDimensionPath(ConfigMetricsReportList *config_report_list) { - for (int i = 0; i < config_report_list->reports_size(); ++i) { - auto report = config_report_list->mutable_reports(i); - for (int j = 0; j < report->metrics_size(); ++j) { - auto metric_report = report->mutable_metrics(j); - if (metric_report->has_dimensions_path_in_what() || - metric_report->has_dimensions_path_in_condition()) { - auto whatPath = metric_report->dimensions_path_in_what(); - auto conditionPath = metric_report->dimensions_path_in_condition(); - if (metric_report->has_count_metrics()) { - backfillDimensionPath(whatPath, conditionPath, - metric_report->mutable_count_metrics()); - } else if (metric_report->has_duration_metrics()) { - backfillDimensionPath(whatPath, conditionPath, - metric_report->mutable_duration_metrics()); - } else if (metric_report->has_gauge_metrics()) { - backfillDimensionPath(whatPath, conditionPath, - metric_report->mutable_gauge_metrics()); - } else if (metric_report->has_value_metrics()) { - backfillDimensionPath(whatPath, conditionPath, - metric_report->mutable_value_metrics()); - } - metric_report->clear_dimensions_path_in_what(); - metric_report->clear_dimensions_path_in_condition(); - } - } - } -} - -void backfillStartEndTimestamp(StatsLogReport *report) { - const int64_t timeBaseNs = report->time_base_elapsed_nano_seconds(); - const int64_t bucketSizeNs = report->bucket_size_nano_seconds(); - if (report->has_count_metrics()) { - backfillStartEndTimestampForMetrics( - timeBaseNs, bucketSizeNs, report->mutable_count_metrics()); - } else if (report->has_duration_metrics()) { - backfillStartEndTimestampForMetrics( - timeBaseNs, bucketSizeNs, report->mutable_duration_metrics()); - } else if (report->has_gauge_metrics()) { - backfillStartEndTimestampForMetrics( - timeBaseNs, bucketSizeNs, report->mutable_gauge_metrics()); - if (report->gauge_metrics().skipped_size() > 0) { - backfillStartEndTimestampForSkippedBuckets( - timeBaseNs, report->mutable_gauge_metrics()); - } - } else if (report->has_value_metrics()) { - backfillStartEndTimestampForMetrics( - timeBaseNs, bucketSizeNs, report->mutable_value_metrics()); - if (report->value_metrics().skipped_size() > 0) { - backfillStartEndTimestampForSkippedBuckets( - timeBaseNs, report->mutable_value_metrics()); - } - } -} - -void backfillStartEndTimestamp(ConfigMetricsReport *config_report) { - for (int j = 0; j < config_report->metrics_size(); ++j) { - backfillStartEndTimestamp(config_report->mutable_metrics(j)); - } -} - -void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list) { - for (int i = 0; i < config_report_list->reports_size(); ++i) { - backfillStartEndTimestamp(config_report_list->mutable_reports(i)); - } -} - -Status FakeSubsystemSleepCallback::onPullAtom(int atomTag, - const shared_ptr<IPullAtomResultReceiver>& resultReceiver) { - // Convert stats_events into StatsEventParcels. - std::vector<StatsEventParcel> parcels; - for (int i = 1; i < 3; i++) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, atomTag); - std::string subsystemName = "subsystem_name_"; - subsystemName = subsystemName + std::to_string(i); - AStatsEvent_writeString(event, subsystemName.c_str()); - AStatsEvent_writeString(event, "subsystem_subname foo"); - AStatsEvent_writeInt64(event, /*count= */ i); - AStatsEvent_writeInt64(event, /*time_millis= */ i * 100); - AStatsEvent_build(event); - size_t size; - uint8_t* buffer = AStatsEvent_getBuffer(event, &size); - - StatsEventParcel p; - // vector.assign() creates a copy, but this is inevitable unless - // stats_event.h/c uses a vector as opposed to a buffer. - p.buffer.assign(buffer, buffer + size); - parcels.push_back(std::move(p)); - AStatsEvent_release(event); - } - resultReceiver->pullFinished(atomTag, /*success=*/true, parcels); - return Status::ok(); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h deleted file mode 100644 index 1220019e2353..000000000000 --- a/cmds/statsd/tests/statsd_test_util.h +++ /dev/null @@ -1,483 +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. - -#pragma once - -#include <aidl/android/os/BnPullAtomCallback.h> -#include <aidl/android/os/IPullAtomCallback.h> -#include <aidl/android/os/IPullAtomResultReceiver.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "src/StatsLogProcessor.h" -#include "src/hash.h" -#include "src/logd/LogEvent.h" -#include "src/matchers/EventMatcherWizard.h" -#include "src/packages/UidMap.h" -#include "src/stats_log_util.h" -#include "stats_event.h" -#include "statslog_statsdtest.h" - -namespace android { -namespace os { -namespace statsd { - -using namespace testing; -using ::aidl::android::os::BnPullAtomCallback; -using ::aidl::android::os::IPullAtomCallback; -using ::aidl::android::os::IPullAtomResultReceiver; -using android::util::ProtoReader; -using google::protobuf::RepeatedPtrField; -using Status = ::ndk::ScopedAStatus; - -const int SCREEN_STATE_ATOM_ID = util::SCREEN_STATE_CHANGED; -const int UID_PROCESS_STATE_ATOM_ID = util::UID_PROCESS_STATE_CHANGED; - -enum BucketSplitEvent { APP_UPGRADE, BOOT_COMPLETE }; - -class MockUidMap : public UidMap { -public: - MOCK_METHOD(int, getHostUidOrSelf, (int uid), (const)); - MOCK_METHOD(std::set<int32_t>, getAppUid, (const string& package), (const)); -}; - -// Converts a ProtoOutputStream to a StatsLogReport proto. -StatsLogReport outputStreamToProto(ProtoOutputStream* proto); - -// Create AtomMatcher proto to simply match a specific atom type. -AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId); - -// Create AtomMatcher proto for temperature atom. -AtomMatcher CreateTemperatureAtomMatcher(); - -// Create AtomMatcher proto for scheduled job state changed. -AtomMatcher CreateScheduledJobStateChangedAtomMatcher(); - -// Create AtomMatcher proto for starting a scheduled job. -AtomMatcher CreateStartScheduledJobAtomMatcher(); - -// Create AtomMatcher proto for a scheduled job is done. -AtomMatcher CreateFinishScheduledJobAtomMatcher(); - -// Create AtomMatcher proto for screen brightness state changed. -AtomMatcher CreateScreenBrightnessChangedAtomMatcher(); - -// Create AtomMatcher proto for starting battery save mode. -AtomMatcher CreateBatterySaverModeStartAtomMatcher(); - -// Create AtomMatcher proto for stopping battery save mode. -AtomMatcher CreateBatterySaverModeStopAtomMatcher(); - -// Create AtomMatcher proto for battery state none mode. -AtomMatcher CreateBatteryStateNoneMatcher(); - -// Create AtomMatcher proto for battery state usb mode. -AtomMatcher CreateBatteryStateUsbMatcher(); - -// Create AtomMatcher proto for process state changed. -AtomMatcher CreateUidProcessStateChangedAtomMatcher(); - -// Create AtomMatcher proto for acquiring wakelock. -AtomMatcher CreateAcquireWakelockAtomMatcher(); - -// Create AtomMatcher proto for releasing wakelock. -AtomMatcher CreateReleaseWakelockAtomMatcher() ; - -// Create AtomMatcher proto for screen turned on. -AtomMatcher CreateScreenTurnedOnAtomMatcher(); - -// Create AtomMatcher proto for screen turned off. -AtomMatcher CreateScreenTurnedOffAtomMatcher(); - -// Create AtomMatcher proto for app sync turned on. -AtomMatcher CreateSyncStartAtomMatcher(); - -// Create AtomMatcher proto for app sync turned off. -AtomMatcher CreateSyncEndAtomMatcher(); - -// Create AtomMatcher proto for app sync moves to background. -AtomMatcher CreateMoveToBackgroundAtomMatcher(); - -// Create AtomMatcher proto for app sync moves to foreground. -AtomMatcher CreateMoveToForegroundAtomMatcher(); - -// Create AtomMatcher proto for process crashes -AtomMatcher CreateProcessCrashAtomMatcher() ; - -// Create Predicate proto for screen is on. -Predicate CreateScreenIsOnPredicate(); - -// Create Predicate proto for screen is off. -Predicate CreateScreenIsOffPredicate(); - -// Create Predicate proto for a running scheduled job. -Predicate CreateScheduledJobPredicate(); - -// Create Predicate proto for battery saver mode. -Predicate CreateBatterySaverModePredicate(); - -// Create Predicate proto for device unplogged mode. -Predicate CreateDeviceUnpluggedPredicate(); - -// Create Predicate proto for holding wakelock. -Predicate CreateHoldingWakelockPredicate(); - -// Create a Predicate proto for app syncing. -Predicate CreateIsSyncingPredicate(); - -// Create a Predicate proto for app is in background. -Predicate CreateIsInBackgroundPredicate(); - -// Create State proto for screen state atom. -State CreateScreenState(); - -// Create State proto for uid process state atom. -State CreateUidProcessState(); - -// Create State proto for overlay state atom. -State CreateOverlayState(); - -// Create State proto for screen state atom with on/off map. -State CreateScreenStateWithOnOffMap(int64_t screenOnId, int64_t screenOffId); - -// Create State proto for screen state atom with simple on/off map. -State CreateScreenStateWithSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId); - -// Create StateGroup proto for ScreenState ON group -StateMap_StateGroup CreateScreenStateOnGroup(int64_t screenOnId); - -// Create StateGroup proto for ScreenState OFF group -StateMap_StateGroup CreateScreenStateOffGroup(int64_t screenOffId); - -// Create StateGroup proto for simple ScreenState ON group -StateMap_StateGroup CreateScreenStateSimpleOnGroup(int64_t screenOnId); - -// Create StateGroup proto for simple ScreenState OFF group -StateMap_StateGroup CreateScreenStateSimpleOffGroup(int64_t screenOffId); - -// Create StateMap proto for ScreenState ON/OFF map -StateMap CreateScreenStateOnOffMap(int64_t screenOnId, int64_t screenOffId); - -// Create StateMap proto for simple ScreenState ON/OFF map -StateMap CreateScreenStateSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId); - -// Add a predicate to the predicate combination. -void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination); - -// Create dimensions from primitive fields. -FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields); - -// Create dimensions by attribution uid and tag. -FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, - const std::vector<Position>& positions); - -// Create dimensions by attribution uid only. -FieldMatcher CreateAttributionUidDimensions(const int atomId, - const std::vector<Position>& positions); - -FieldMatcher CreateAttributionUidAndOtherDimensions(const int atomId, - const std::vector<Position>& positions, - const std::vector<int>& fields); - -// START: get primary key functions -// These functions take in atom field information and create FieldValues which are stored in the -// given HashableDimensionKey. -void getUidProcessKey(int uid, HashableDimensionKey* key); - -void getOverlayKey(int uid, string packageName, HashableDimensionKey* key); - -void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key); - -void getPartialWakelockKey(int uid, HashableDimensionKey* key); -// END: get primary key functions - -void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids, - const vector<string>& attributionTags); - -// Builds statsEvent to get buffer that is parsed into logEvent then releases statsEvent. -void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent); - -shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2); - -void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2); - -shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2, int32_t value3); - -void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2, int32_t value3); - -// The repeated value log event helpers create a log event with two int fields, both -// set to the same value. This is useful for testing metrics that are only interested -// in the value of the second field but still need the first field to be populated. -std::shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, - int32_t value); - -void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, - int32_t value); - -std::shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs); - -void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs); - -std::shared_ptr<LogEvent> makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1, - int data2); - -std::shared_ptr<LogEvent> makeAttributionLogEvent(int atomId, int64_t eventTimeNs, - const vector<int>& uids, - const vector<string>& tags, int data1, int data2); - -sp<MockUidMap> makeMockUidMapForOneHost(int hostUid, const vector<int>& isolatedUids); - -sp<MockUidMap> makeMockUidMapForPackage(const string& pkg, const set<int32_t>& uids); - -// Create log event for screen state changed. -std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(uint64_t timestampNs, - const android::view::DisplayStateEnum state, - int loggerUid = 0); - -// Create log event for screen brightness state changed. -std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level); - -// Create log event when scheduled job starts. -std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& jobName); - -// Create log event when scheduled job finishes. -std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const string& jobName); - -// Create log event when battery saver starts. -std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs); -// Create log event when battery saver stops. -std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs); - -// Create log event when battery state changes. -std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state); - -// Create log event for app moving to background. -std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid); - -// Create log event for app moving to foreground. -std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid); - -// Create log event when the app sync starts. -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(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(uint64_t timestampNs, const int uid); - -// Create log event for an app crash. -std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid); - -// Create log event for acquiring wakelock. -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(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(uint64_t timestampNs, int hostUid, - int isolatedUid, bool is_create); - -// Create log event for uid process state change. -std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( - uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state); - -std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs, - const vector<int>& attributionUids, - const vector<string>& attributionTags, - const BleScanStateChanged::State state, - const bool filtered, const bool firstMatch, - const bool opportunistic); - -std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid, - const string& packageName, - const bool usingAlertWindow, - const OverlayStateChanged::State state); - -// Create a statsd log event processor upon the start time in seconds, config and key. -sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs, - const StatsdConfig& config, const ConfigKey& key, - const shared_ptr<IPullAtomCallback>& puller = nullptr, - const int32_t atomTag = 0 /*for puller only*/, - const sp<UidMap> = new UidMap()); - -// Util function to sort the log events by timestamp. -void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events); - -int64_t StringToId(const string& str); - -sp<EventMatcherWizard> createEventMatcherWizard( - int tagId, int matcherIndex, const std::vector<FieldValueMatcher>& fieldValueMatchers = {}); - -void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId, - const int uid, const string& tag); -void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid); -void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid); -void ValidateAttributionUidAndTagDimension( - const DimensionsValue& value, int atomId, int uid, const std::string& tag); -void ValidateAttributionUidAndTagDimension( - const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag); - -struct DimensionsPair { - DimensionsPair(DimensionsValue m1, google::protobuf::RepeatedPtrField<StateValue> m2) - : dimInWhat(m1), stateValues(m2){}; - - DimensionsValue dimInWhat; - google::protobuf::RepeatedPtrField<StateValue> stateValues; -}; - -bool LessThan(const StateValue& s1, const StateValue& s2); -bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2); -bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2); - - -void backfillStartEndTimestamp(ConfigMetricsReport *config_report); -void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list); - -void backfillStringInReport(ConfigMetricsReportList *config_report_list); -void backfillStringInDimension(const std::map<uint64_t, string>& str_map, - DimensionsValue* dimension); - -template <typename T> -void backfillStringInDimension(const std::map<uint64_t, string>& str_map, - T* metrics) { - for (int i = 0; i < metrics->data_size(); ++i) { - auto data = metrics->mutable_data(i); - if (data->has_dimensions_in_what()) { - backfillStringInDimension(str_map, data->mutable_dimensions_in_what()); - } - if (data->has_dimensions_in_condition()) { - backfillStringInDimension(str_map, data->mutable_dimensions_in_condition()); - } - } -} - -void backfillDimensionPath(ConfigMetricsReportList* config_report_list); - -bool backfillDimensionPath(const DimensionsValue& path, - const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues, - DimensionsValue* dimension); - -class FakeSubsystemSleepCallback : public BnPullAtomCallback { -public: - Status onPullAtom(int atomTag, - const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override; -}; - -template <typename T> -void backfillDimensionPath(const DimensionsValue& whatPath, - const DimensionsValue& conditionPath, - T* metricData) { - for (int i = 0; i < metricData->data_size(); ++i) { - auto data = metricData->mutable_data(i); - if (data->dimension_leaf_values_in_what_size() > 0) { - backfillDimensionPath(whatPath, data->dimension_leaf_values_in_what(), - data->mutable_dimensions_in_what()); - data->clear_dimension_leaf_values_in_what(); - } - if (data->dimension_leaf_values_in_condition_size() > 0) { - backfillDimensionPath(conditionPath, data->dimension_leaf_values_in_condition(), - data->mutable_dimensions_in_condition()); - data->clear_dimension_leaf_values_in_condition(); - } - } -} - -struct DimensionCompare { - bool operator()(const DimensionsPair& s1, const DimensionsPair& s2) const { - return LessThan(s1, s2); - } -}; - -template <typename T> -void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) { - std::map<DimensionsPair, int, DimensionCompare> dimensionIndexMap; - for (int i = 0; i < metricData.data_size(); ++i) { - dimensionIndexMap.insert( - std::make_pair(DimensionsPair(metricData.data(i).dimensions_in_what(), - metricData.data(i).slice_by_state()), - i)); - } - for (const auto& itr : dimensionIndexMap) { - *sortedMetricData->add_data() = metricData.data(itr.second); - } -} - -template <typename T> -void backfillStartEndTimestampForFullBucket( - const int64_t timeBaseNs, const int64_t bucketSizeNs, T* bucket) { - bucket->set_start_bucket_elapsed_nanos(timeBaseNs + bucketSizeNs * bucket->bucket_num()); - bucket->set_end_bucket_elapsed_nanos( - timeBaseNs + bucketSizeNs * bucket->bucket_num() + bucketSizeNs); - bucket->clear_bucket_num(); -} - -template <typename T> -void backfillStartEndTimestampForPartialBucket(const int64_t timeBaseNs, T* bucket) { - if (bucket->has_start_bucket_elapsed_millis()) { - bucket->set_start_bucket_elapsed_nanos( - MillisToNano(bucket->start_bucket_elapsed_millis())); - bucket->clear_start_bucket_elapsed_millis(); - } - if (bucket->has_end_bucket_elapsed_millis()) { - bucket->set_end_bucket_elapsed_nanos( - MillisToNano(bucket->end_bucket_elapsed_millis())); - bucket->clear_end_bucket_elapsed_millis(); - } -} - -template <typename T> -void backfillStartEndTimestampForMetrics(const int64_t timeBaseNs, const int64_t bucketSizeNs, - T* metrics) { - for (int i = 0; i < metrics->data_size(); ++i) { - auto data = metrics->mutable_data(i); - for (int j = 0; j < data->bucket_info_size(); ++j) { - auto bucket = data->mutable_bucket_info(j); - if (bucket->has_bucket_num()) { - backfillStartEndTimestampForFullBucket(timeBaseNs, bucketSizeNs, bucket); - } else { - backfillStartEndTimestampForPartialBucket(timeBaseNs, bucket); - } - } - } -} - -template <typename T> -void backfillStartEndTimestampForSkippedBuckets(const int64_t timeBaseNs, T* metrics) { - for (int i = 0; i < metrics->skipped_size(); ++i) { - backfillStartEndTimestampForPartialBucket(timeBaseNs, metrics->mutable_skipped(i)); - } -} -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp deleted file mode 100644 index 74eafbf56d66..000000000000 --- a/cmds/statsd/tests/storage/StorageManager_test.cpp +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <android-base/unique_fd.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <stdio.h> -#include "src/storage/StorageManager.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -using namespace testing; -using std::make_shared; -using std::shared_ptr; -using std::vector; -using testing::Contains; - -TEST(StorageManagerTest, TrainInfoReadWriteTest) { - InstallTrainInfo trainInfo; - trainInfo.trainVersionCode = 12345; - trainInfo.trainName = "This is a train name #)$(&&$"; - trainInfo.status = 1; - const char* expIds = "test_ids"; - trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds)); - - bool result; - - result = StorageManager::writeTrainInfo(trainInfo); - - EXPECT_TRUE(result); - - InstallTrainInfo trainInfoResult; - result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult); - EXPECT_TRUE(result); - - EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode); - ASSERT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size()); - EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName); - EXPECT_EQ(trainInfo.status, trainInfoResult.status); - ASSERT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size()); - EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds); -} - -TEST(StorageManagerTest, TrainInfoReadWriteTrainNameSizeOneTest) { - InstallTrainInfo trainInfo; - trainInfo.trainVersionCode = 12345; - trainInfo.trainName = "{"; - trainInfo.status = 1; - const char* expIds = "test_ids"; - trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds)); - - bool result; - - result = StorageManager::writeTrainInfo(trainInfo); - - EXPECT_TRUE(result); - - InstallTrainInfo trainInfoResult; - result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult); - EXPECT_TRUE(result); - - EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode); - ASSERT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size()); - EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName); - EXPECT_EQ(trainInfo.status, trainInfoResult.status); - ASSERT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size()); - EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds); -} - -TEST(StorageManagerTest, SortFileTest) { - vector<StorageManager::FileInfo> list; - // assume now sec is 500 - list.emplace_back("200_5000_123454", false, 20, 300); - list.emplace_back("300_2000_123454_history", true, 30, 200); - list.emplace_back("400_100009_123454_history", true, 40, 100); - list.emplace_back("100_2000_123454", false, 50, 400); - - StorageManager::sortFiles(&list); - EXPECT_EQ("200_5000_123454", list[0].mFileName); - EXPECT_EQ("100_2000_123454", list[1].mFileName); - EXPECT_EQ("400_100009_123454_history", list[2].mFileName); - EXPECT_EQ("300_2000_123454_history", list[3].mFileName); -} - -const string testDir = "/data/misc/stats-data/"; -const string file1 = testDir + "2557169347_1066_1"; -const string file2 = testDir + "2557169349_1066_1"; -const string file1_history = file1 + "_history"; -const string file2_history = file2 + "_history"; - -bool prepareLocalHistoryTestFiles() { - android::base::unique_fd fd(TEMP_FAILURE_RETRY( - open(file1.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR))); - if (fd != -1) { - dprintf(fd, "content"); - } else { - return false; - } - - android::base::unique_fd fd2(TEMP_FAILURE_RETRY( - open(file2.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR))); - if (fd2 != -1) { - dprintf(fd2, "content"); - } else { - return false; - } - return true; -} - -void clearLocalHistoryTestFiles() { - TEMP_FAILURE_RETRY(remove(file1.c_str())); - TEMP_FAILURE_RETRY(remove(file2.c_str())); - TEMP_FAILURE_RETRY(remove(file1_history.c_str())); - TEMP_FAILURE_RETRY(remove(file2_history.c_str())); -} - -bool fileExist(string name) { - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY | O_CLOEXEC))); - return fd != -1; -} - -/* The following AppendConfigReportTests test the 4 combinations of [whether erase data] [whether - * the caller is adb] */ -TEST(StorageManagerTest, AppendConfigReportTest1) { - EXPECT_TRUE(prepareLocalHistoryTestFiles()); - - ProtoOutputStream out; - StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, false /*erase?*/, - false /*isAdb?*/); - - EXPECT_FALSE(fileExist(file1)); - EXPECT_FALSE(fileExist(file2)); - - EXPECT_TRUE(fileExist(file1_history)); - EXPECT_TRUE(fileExist(file2_history)); - clearLocalHistoryTestFiles(); -} - -TEST(StorageManagerTest, AppendConfigReportTest2) { - EXPECT_TRUE(prepareLocalHistoryTestFiles()); - - ProtoOutputStream out; - StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, true /*erase?*/, - false /*isAdb?*/); - - EXPECT_FALSE(fileExist(file1)); - EXPECT_FALSE(fileExist(file2)); - EXPECT_FALSE(fileExist(file1_history)); - EXPECT_FALSE(fileExist(file2_history)); - - clearLocalHistoryTestFiles(); -} - -TEST(StorageManagerTest, AppendConfigReportTest3) { - EXPECT_TRUE(prepareLocalHistoryTestFiles()); - - ProtoOutputStream out; - StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, false /*erase?*/, - true /*isAdb?*/); - - EXPECT_TRUE(fileExist(file1)); - EXPECT_TRUE(fileExist(file2)); - EXPECT_FALSE(fileExist(file1_history)); - EXPECT_FALSE(fileExist(file2_history)); - - clearLocalHistoryTestFiles(); -} - -TEST(StorageManagerTest, AppendConfigReportTest4) { - EXPECT_TRUE(prepareLocalHistoryTestFiles()); - - ProtoOutputStream out; - StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, true /*erase?*/, - true /*isAdb?*/); - - EXPECT_FALSE(fileExist(file1)); - EXPECT_FALSE(fileExist(file2)); - EXPECT_FALSE(fileExist(file1_history)); - EXPECT_FALSE(fileExist(file2_history)); - - clearLocalHistoryTestFiles(); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp deleted file mode 100644 index 32cecd3b9dbc..000000000000 --- a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp +++ /dev/null @@ -1,174 +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. - */ -#include "utils/MultiConditionTrigger.h" - -#include <gtest/gtest.h> - -#include <chrono> -#include <set> -#include <thread> -#include <vector> - -#ifdef __ANDROID__ - -using namespace std; -using std::this_thread::sleep_for; - -namespace android { -namespace os { -namespace statsd { - -TEST(MultiConditionTrigger, TestMultipleConditions) { - int numConditions = 5; - string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5"; - set<string> conditionNames = {t1, t2, t3, t4, t5}; - - mutex lock; - condition_variable cv; - bool triggerCalled = false; - - // Mark done as true and notify in the done. - MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] { - { - lock_guard lg(lock); - triggerCalled = true; - } - cv.notify_all(); - }); - - vector<thread> threads; - vector<int> done(numConditions, 0); - - int i = 0; - for (const string& conditionName : conditionNames) { - threads.emplace_back([&done, &conditionName, &trigger, i] { - sleep_for(chrono::milliseconds(3)); - done[i] = 1; - trigger.markComplete(conditionName); - }); - i++; - } - - unique_lock<mutex> unique_lk(lock); - cv.wait(unique_lk, [&triggerCalled] { - return triggerCalled; - }); - - for (i = 0; i < numConditions; i++) { - EXPECT_EQ(done[i], 1); - } - - for (i = 0; i < numConditions; i++) { - threads[i].join(); - } -} - -TEST(MultiConditionTrigger, TestNoConditions) { - mutex lock; - condition_variable cv; - bool triggerCalled = false; - - MultiConditionTrigger trigger({}, [&lock, &cv, &triggerCalled] { - { - lock_guard lg(lock); - triggerCalled = true; - } - cv.notify_all(); - }); - - unique_lock<mutex> unique_lk(lock); - cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; }); - EXPECT_TRUE(triggerCalled); - // Ensure that trigger occurs immediately if no events need to be completed. -} - -TEST(MultiConditionTrigger, TestMarkCompleteCalledBySameCondition) { - string t1 = "t1", t2 = "t2"; - set<string> conditionNames = {t1, t2}; - - mutex lock; - condition_variable cv; - bool triggerCalled = false; - - MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] { - { - lock_guard lg(lock); - triggerCalled = true; - } - cv.notify_all(); - }); - - trigger.markComplete(t1); - trigger.markComplete(t1); - - // Ensure that the trigger still hasn't fired. - { - lock_guard lg(lock); - EXPECT_FALSE(triggerCalled); - } - - trigger.markComplete(t2); - unique_lock<mutex> unique_lk(lock); - cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; }); - EXPECT_TRUE(triggerCalled); -} - -TEST(MultiConditionTrigger, TestTriggerOnlyCalledOnce) { - string t1 = "t1"; - set<string> conditionNames = {t1}; - - mutex lock; - condition_variable cv; - bool triggerCalled = false; - int triggerCount = 0; - - MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled, &triggerCount] { - { - lock_guard lg(lock); - triggerCount++; - triggerCalled = true; - } - cv.notify_all(); - }); - - trigger.markComplete(t1); - - // Ensure that the trigger fired. - { - unique_lock<mutex> unique_lk(lock); - cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; }); - EXPECT_TRUE(triggerCalled); - EXPECT_EQ(triggerCount, 1); - triggerCalled = false; - } - - trigger.markComplete(t1); - - // Ensure that the trigger does not fire again. - { - unique_lock<mutex> unique_lk(lock); - cv.wait_for(unique_lk, chrono::milliseconds(5), [&triggerCalled] { return triggerCalled; }); - EXPECT_FALSE(triggerCalled); - EXPECT_EQ(triggerCount, 1); - } -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tools/localtools/Android.bp b/cmds/statsd/tools/localtools/Android.bp deleted file mode 100644 index 69a43a8f4712..000000000000 --- a/cmds/statsd/tools/localtools/Android.bp +++ /dev/null @@ -1,46 +0,0 @@ -java_binary_host { - name: "statsd_localdrive", - manifest: "localdrive_manifest.txt", - srcs: [ - "src/com/android/statsd/shelltools/localdrive/*.java", - "src/com/android/statsd/shelltools/Utils.java", - ], - static_libs: [ - "platformprotos", - "guava", - ], -} - -java_library_host { - name: "statsd_testdrive_lib", - srcs: [ - "src/com/android/statsd/shelltools/testdrive/*.java", - "src/com/android/statsd/shelltools/Utils.java", - ], - static_libs: [ - "platformprotos", - "guava", - ], -} - - -java_binary_host { - name: "statsd_testdrive", - manifest: "testdrive_manifest.txt", - static_libs: [ - "statsd_testdrive_lib", - ], -} - -java_test_host { - name: "statsd_testdrive_test", - test_suites: ["general-tests"], - srcs: ["test/com/android/statsd/shelltools/testdrive/*.java"], - static_libs: [ - "statsd_testdrive_lib", - "junit", - "platformprotos", - "guava", - ], -} - diff --git a/cmds/statsd/tools/localtools/TEST_MAPPING b/cmds/statsd/tools/localtools/TEST_MAPPING deleted file mode 100644 index 7c8a3db5c610..000000000000 --- a/cmds/statsd/tools/localtools/TEST_MAPPING +++ /dev/null @@ -1,8 +0,0 @@ -{ - "presubmit": [ - { - "name": "statsd_testdrive_test", - "host": true - } - ] -} diff --git a/cmds/statsd/tools/localtools/localdrive_manifest.txt b/cmds/statsd/tools/localtools/localdrive_manifest.txt deleted file mode 100644 index 035cea1134bc..000000000000 --- a/cmds/statsd/tools/localtools/localdrive_manifest.txt +++ /dev/null @@ -1 +0,0 @@ -Main-class: com.android.statsd.shelltools.localdrive.LocalDrive diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java deleted file mode 100644 index 6a74480b505e..000000000000 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * 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.statsd.shelltools; - -import com.android.os.StatsLog.ConfigMetricsReportList; - -import com.google.common.io.Files; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.ConsoleHandler; -import java.util.logging.Formatter; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Utilities for local use of statsd. - */ -public class Utils { - - public static final String CMD_DUMP_REPORT = "cmd stats dump-report"; - public static final String CMD_LOG_APP_BREADCRUMB = "cmd stats log-app-breadcrumb"; - public static final String CMD_REMOVE_CONFIG = "cmd stats config remove"; - public static final String CMD_UPDATE_CONFIG = "cmd stats config update"; - - public static final String SHELL_UID = "2000"; // Use shell, even if rooted. - - /** - * Runs adb shell command with output directed to outputFile if non-null. - */ - public static void runCommand(File outputFile, Logger logger, String... commands) - throws IOException, InterruptedException { - ProcessBuilder pb = new ProcessBuilder(commands); - if (outputFile != null && outputFile.exists() && outputFile.canWrite()) { - pb.redirectOutput(outputFile); - } - Process process = pb.start(); - - // Capture any errors - StringBuilder err = new StringBuilder(); - BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream())); - for (String line = br.readLine(); line != null; line = br.readLine()) { - err.append(line).append('\n'); - } - logger.severe(err.toString()); - - // Check result - if (process.waitFor() == 0) { - logger.fine("Adb command successful."); - } else { - logger.severe("Abnormal adb shell termination for: " + String.join(",", commands)); - throw new RuntimeException("Error running adb command: " + err.toString()); - } - } - - /** - * Dumps the report from the device and converts it to a ConfigMetricsReportList. - * Erases the data if clearData is true. - * @param configId id of the config - * @param clearData whether to erase the report data from statsd after getting the report. - * @param useShellUid Pulls data for the {@link SHELL_UID} instead of the caller's uid. - * @param logger Logger to log error messages - * @return - * @throws IOException - * @throws InterruptedException - */ - public static ConfigMetricsReportList getReportList(long configId, boolean clearData, - boolean useShellUid, Logger logger, String deviceSerial) - throws IOException, InterruptedException { - try { - File outputFile = File.createTempFile("statsdret", ".bin"); - outputFile.deleteOnExit(); - runCommand( - outputFile, - logger, - "adb", - "-s", - deviceSerial, - "shell", - CMD_DUMP_REPORT, - useShellUid ? SHELL_UID : "", - String.valueOf(configId), - clearData ? "" : "--keep_data", - "--include_current_bucket", - "--proto"); - ConfigMetricsReportList reportList = - ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile)); - return reportList; - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - logger.severe("Failed to fetch and parse the statsd output report. " - + "Perhaps there is not a valid statsd config for the requested " - + (useShellUid ? ("uid=" + SHELL_UID + ", ") : "") - + "configId=" + configId - + "."); - throw (e); - } - } - - /** - * Logs an AppBreadcrumbReported atom. - * @param label which label to log for the app breadcrumb atom. - * @param state which state to log for the app breadcrumb atom. - * @param logger Logger to log error messages - * - * @throws IOException - * @throws InterruptedException - */ - public static void logAppBreadcrumb(int label, int state, Logger logger, String deviceSerial) - throws IOException, InterruptedException { - runCommand( - null, - logger, - "adb", - "-s", - deviceSerial, - "shell", - CMD_LOG_APP_BREADCRUMB, - String.valueOf(label), - String.valueOf(state)); - } - public static void setUpLogger(Logger logger, boolean debug) { - ConsoleHandler handler = new ConsoleHandler(); - handler.setFormatter(new LocalToolsFormatter()); - logger.setUseParentHandlers(false); - if (debug) { - handler.setLevel(Level.ALL); - logger.setLevel(Level.ALL); - } - logger.addHandler(handler); - } - - /** - * Attempt to determine whether tool will work with this statsd, i.e. whether statsd is - * minCodename or higher. - * Algorithm: true if (sdk >= minSdk) || (sdk == minSdk-1 && codeName.startsWith(minCodeName)) - * If all else fails, assume it will work (letting future commands deal with any errors). - */ - public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename, - String deviceSerial) { - BufferedReader in = null; - try { - File outFileSdk = File.createTempFile("shelltools_sdk", "tmp"); - outFileSdk.deleteOnExit(); - runCommand(outFileSdk, logger, - "adb", "-s", deviceSerial, "shell", "getprop", "ro.build.version.sdk"); - in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileSdk))); - // If NullPointerException/NumberFormatException/etc., just catch and return true. - int sdk = Integer.parseInt(in.readLine().trim()); - if (sdk >= minSdk) { - return true; - } else if (sdk == minSdk - 1) { // Could be minSdk-1, or could be minSdk development. - in.close(); - File outFileCode = File.createTempFile("shelltools_codename", "tmp"); - outFileCode.deleteOnExit(); - runCommand(outFileCode, logger, - "adb", "-s", deviceSerial, "shell", "getprop", "ro.build.version.codename"); - in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileCode))); - return in.readLine().startsWith(minCodename); - } else { - return false; - } - } catch (Exception e) { - logger.fine("Could not determine whether statsd version is compatibile " - + "with tool: " + e.toString()); - } finally { - try { - if (in != null) { - in.close(); - } - } catch (IOException e) { - logger.fine("Could not close temporary file: " + e.toString()); - } - } - // Could not determine whether statsd is acceptable version. - // Just assume it is; if it isn't, we'll just get future errors via adb and deal with them. - return true; - } - - public static class LocalToolsFormatter extends Formatter { - public String format(LogRecord record) { - return record.getMessage() + "\n"; - } - } - - /** - * Parse the result of "adb devices" to return the list of connected devices. - * @param logger Logger to log error messages - * @return List of the serial numbers of the connected devices. - */ - public static List<String> getDeviceSerials(Logger logger) { - try { - ArrayList<String> devices = new ArrayList<>(); - File outFile = File.createTempFile("device_serial", "tmp"); - outFile.deleteOnExit(); - Utils.runCommand(outFile, logger, "adb", "devices"); - List<String> outputLines = Files.readLines(outFile, Charset.defaultCharset()); - Pattern regex = Pattern.compile("^(.*)\tdevice$"); - for (String line : outputLines) { - Matcher m = regex.matcher(line); - if (m.find()) { - devices.add(m.group(1)); - } - } - return devices; - } catch (Exception ex) { - logger.log(Level.SEVERE, "Failed to list connected devices: " + ex.getMessage()); - } - return null; - } - - /** - * Returns ANDROID_SERIAL environment variable, or null if that is undefined or unavailable. - * @param logger Destination of error messages. - * @return String value of ANDROID_SERIAL environment variable, or null. - */ - public static String getDefaultDevice(Logger logger) { - try { - return System.getenv("ANDROID_SERIAL"); - } catch (Exception ex) { - logger.log(Level.SEVERE, "Failed to check ANDROID_SERIAL environment variable.", - ex); - } - return null; - } - - /** - * Returns the device to use if one can be deduced, or null. - * @param device Command-line specified device, or null. - * @param connectedDevices List of all connected devices. - * @param defaultDevice Environment-variable specified device, or null. - * @param logger Destination of error messages. - * @return Device to use, or null. - */ - public static String chooseDevice(String device, List<String> connectedDevices, - String defaultDevice, Logger logger) { - if (connectedDevices == null || connectedDevices.isEmpty()) { - logger.severe("No connected device."); - return null; - } - if (device != null) { - if (connectedDevices.contains(device)) { - return device; - } - logger.severe("Device not connected: " + device); - return null; - } - if (connectedDevices.size() == 1) { - return connectedDevices.get(0); - } - if (defaultDevice != null) { - if (connectedDevices.contains(defaultDevice)) { - return defaultDevice; - } else { - logger.severe("ANDROID_SERIAL device is not connected: " + defaultDevice); - return null; - } - } - logger.severe("More than one device is connected. Choose one" - + " with -s DEVICE_SERIAL or environment variable ANDROID_SERIAL."); - return null; - } -} diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java deleted file mode 100644 index ec3c7df7bfba..000000000000 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * 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.statsd.shelltools.localdrive; - -import com.android.internal.os.StatsdConfigProto.StatsdConfig; -import com.android.os.StatsLog.ConfigMetricsReport; -import com.android.os.StatsLog.ConfigMetricsReportList; -import com.android.statsd.shelltools.Utils; - -import com.google.common.io.Files; -import com.google.protobuf.TextFormat; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.List; -import java.util.logging.Logger; - -/** - * Tool for using statsd locally. Can upload a config and get the data. Handles - * both binary and human-readable protos. - * To make: make statsd_localdrive - * To run: statsd_localdrive (i.e. ./out/host/linux-x86/bin/statsd_localdrive) - */ -public class LocalDrive { - private static final boolean DEBUG = false; - - public static final int MIN_SDK = 29; - public static final String MIN_CODENAME = "Q"; - - public static final long DEFAULT_CONFIG_ID = 56789; - - public static final String BINARY_FLAG = "--binary"; - public static final String CLEAR_DATA = "--clear"; - public static final String NO_UID_MAP_FLAG = "--no-uid-map"; - - public static final String HELP_STRING = - "Usage:\n\n" + - - "statsd_localdrive [-s DEVICE_SERIAL] upload CONFIG_FILE [CONFIG_ID] [--binary]\n" + - " Uploads the given statsd config file (in binary or human-readable-text format).\n" + - " If a config with this id already exists, removes it first.\n" + - " CONFIG_FILE Location of config file on host.\n" + - " CONFIG_ID Long ID to associate with this config. If absent, uses " - + DEFAULT_CONFIG_ID + ".\n" + - " --binary Config is in binary format; otherwise, assumed human-readable text.\n" + - // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID - "\n" + - - "statsd_localdrive [-s DEVICE_SERIAL] update CONFIG_FILE [CONFIG_ID] [--binary]\n" + - " Same as upload, but does not remove the old config first (if it already exists).\n" + - // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID - "\n" + - - "statsd_localdrive [-s DEVICE_SERIAL] get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" + - " Prints the output statslog data (in binary or human-readable-text format).\n" + - " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" + - " --binary Output should be in binary, instead of default human-readable text.\n" + - " Binary output can be redirected as usual (e.g. > FILENAME).\n" + - " --no-uid-map Do not include the uid-map (the very lengthy uid<-->pkgName map).\n" + - " --clear Erase the data from statsd afterwards. Does not remove the config.\n" + - // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID [--keep_data] - // --include_current_bucket --proto - "\n" + - - "statsd_localdrive [-s DEVICE_SERIAL] remove [CONFIG_ID]\n" + - " Removes the config.\n" + - " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" + - // Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID - "\n" + - - "statsd_localdrive [-s DEVICE_SERIAL] clear [CONFIG_ID]\n" + - " Clears the data associated with the config.\n" + - " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" + - // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID - // --include_current_bucket --proto - ""; - - - private static final Logger sLogger = Logger.getLogger(LocalDrive.class.getName()); - - /** Usage: make statsd_localdrive && statsd_localdrive */ - public static void main(String[] args) { - Utils.setUpLogger(sLogger, DEBUG); - if (args.length == 0) { - printHelp(); - return; - } - - int remainingArgsLength = args.length; - String deviceSerial = null; - if (args[0].equals("-s")) { - if (args.length == 1) { - printHelp(); - } - deviceSerial = args[1]; - remainingArgsLength -= 2; - } - - List<String> connectedDevices = Utils.getDeviceSerials(sLogger); - deviceSerial = Utils.chooseDevice(deviceSerial, connectedDevices, - Utils.getDefaultDevice(sLogger), sLogger); - if (deviceSerial == null) { - return; - } - - if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME, deviceSerial)) { - sLogger.severe("LocalDrive only works with statsd versions for Android " - + MIN_CODENAME + " or higher."); - return; - } - - int idx = args.length - remainingArgsLength; - if (remainingArgsLength > 0) { - switch (args[idx]) { - case "clear": - cmdClear(args, idx, deviceSerial); - return; - case "get-data": - cmdGetData(args, idx, deviceSerial); - return; - case "remove": - cmdRemove(args, idx); - return; - case "update": - cmdUpdate(args, idx, deviceSerial); - return; - case "upload": - cmdUpload(args, idx, deviceSerial); - return; - } - } - printHelp(); - } - - private static void printHelp() { - sLogger.info(HELP_STRING); - } - - // upload CONFIG_FILE [CONFIG_ID] [--binary] - private static boolean cmdUpload(String[] args, int idx, String deviceSerial) { - return updateConfig(args, idx, true, deviceSerial); - } - - // update CONFIG_FILE [CONFIG_ID] [--binary] - private static boolean cmdUpdate(String[] args, int idx, String deviceSerial) { - return updateConfig(args, idx, false, deviceSerial); - } - - private static boolean updateConfig(String[] args, int idx, boolean removeOldConfig, - String deviceSerial) { - int argCount = args.length - 1 - idx; // Used up one for upload/update. - - // Get CONFIG_FILE - if (argCount < 1) { - sLogger.severe("No config file provided."); - printHelp(); - return false; - } - final String origConfigLocation = args[idx + 1]; - if (!new File(origConfigLocation).exists()) { - sLogger.severe("Error - Cannot find the provided config file: " + origConfigLocation); - return false; - } - argCount--; - - // Get --binary - boolean binary = contains(args, idx + 2, BINARY_FLAG); - if (binary) argCount --; - - // Get CONFIG_ID - long configId; - try { - configId = getConfigId(argCount < 1, args, idx + 2); - } catch (NumberFormatException e) { - sLogger.severe("Invalid config id provided."); - printHelp(); - return false; - } - sLogger.fine(String.format("updateConfig with %s %d %b %b", - origConfigLocation, configId, binary, removeOldConfig)); - - // Remove the old config. - if (removeOldConfig) { - try { - Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG, - Utils.SHELL_UID, String.valueOf(configId)); - Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger, - deviceSerial); - } catch (InterruptedException | IOException e) { - sLogger.severe("Failed to remove config: " + e.getMessage()); - return false; - } - } - - // Upload the config. - String configLocation; - if (binary) { - configLocation = origConfigLocation; - } else { - StatsdConfig.Builder builder = StatsdConfig.newBuilder(); - try { - TextFormat.merge(new FileReader(origConfigLocation), builder); - } catch (IOException e) { - sLogger.severe("Failed to read config file " + origConfigLocation + ": " - + e.getMessage()); - return false; - } - - try { - File tempConfigFile = File.createTempFile("statsdconfig", ".config"); - tempConfigFile.deleteOnExit(); - Files.write(builder.build().toByteArray(), tempConfigFile); - configLocation = tempConfigFile.getAbsolutePath(); - } catch (IOException e) { - sLogger.severe("Failed to write temp config file: " + e.getMessage()); - return false; - } - } - String remotePath = "/data/local/tmp/statsdconfig.config"; - try { - Utils.runCommand(null, sLogger, "adb", "push", configLocation, remotePath); - Utils.runCommand(null, sLogger, "adb", "shell", "cat", remotePath, "|", - Utils.CMD_UPDATE_CONFIG, Utils.SHELL_UID, String.valueOf(configId)); - } catch (InterruptedException | IOException e) { - sLogger.severe("Failed to update config: " + e.getMessage()); - return false; - } - return true; - } - - // get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map] - private static boolean cmdGetData(String[] args, int idx, String deviceSerial) { - boolean binary = contains(args, idx + 1, BINARY_FLAG); - boolean noUidMap = contains(args, idx + 1, NO_UID_MAP_FLAG); - boolean clearData = contains(args, idx + 1, CLEAR_DATA); - - // Get CONFIG_ID - int argCount = args.length - 1 - idx; // Used up one for get-data. - if (binary) argCount--; - if (noUidMap) argCount--; - if (clearData) argCount--; - long configId; - try { - configId = getConfigId(argCount < 1, args, idx + 1); - } catch (NumberFormatException e) { - sLogger.severe("Invalid config id provided."); - printHelp(); - return false; - } - sLogger.fine(String.format("cmdGetData with %d %b %b %b", - configId, clearData, binary, noUidMap)); - - // Get the StatsLog - // Even if the args request no modifications, we still parse it to make sure it's valid. - ConfigMetricsReportList reportList; - try { - reportList = Utils.getReportList(configId, clearData, true /* SHELL_UID */, sLogger, - deviceSerial); - } catch (IOException | InterruptedException e) { - sLogger.severe("Failed to get report list: " + e.getMessage()); - return false; - } - if (noUidMap) { - ConfigMetricsReportList.Builder builder - = ConfigMetricsReportList.newBuilder(reportList); - // Clear the reports, then add them back without their UidMap. - builder.clearReports(); - for (ConfigMetricsReport report : reportList.getReportsList()) { - builder.addReports(ConfigMetricsReport.newBuilder(report).clearUidMap()); - } - reportList = builder.build(); - } - - if (!binary) { - sLogger.info(reportList.toString()); - } else { - try { - System.out.write(reportList.toByteArray()); - } catch (IOException e) { - sLogger.severe("Failed to output binary statslog proto: " - + e.getMessage()); - return false; - } - } - return true; - } - - // clear [CONFIG_ID] - private static boolean cmdClear(String[] args, int idx, String deviceSerial) { - // Get CONFIG_ID - long configId; - try { - configId = getConfigId(false, args, idx + 1); - } catch (NumberFormatException e) { - sLogger.severe("Invalid config id provided."); - printHelp(); - return false; - } - sLogger.fine(String.format("cmdClear with %d", configId)); - - try { - Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger, - deviceSerial); - } catch (IOException | InterruptedException e) { - sLogger.severe("Failed to get report list: " + e.getMessage()); - return false; - } - return true; - } - - // remove [CONFIG_ID] - private static boolean cmdRemove(String[] args, int idx) { - // Get CONFIG_ID - long configId; - try { - configId = getConfigId(false, args, idx + 1); - } catch (NumberFormatException e) { - sLogger.severe("Invalid config id provided."); - printHelp(); - return false; - } - sLogger.fine(String.format("cmdRemove with %d", configId)); - - try { - Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG, - Utils.SHELL_UID, String.valueOf(configId)); - } catch (InterruptedException | IOException e) { - sLogger.severe("Failed to remove config: " + e.getMessage()); - return false; - } - return true; - } - - /** - * Searches through the array to see if it contains (precisely) the given value, starting - * at the given firstIdx. - */ - private static boolean contains(String[] array, int firstIdx, String value) { - if (value == null) return false; - if (firstIdx < 0) return false; - for (int i = firstIdx; i < array.length; i++) { - if (value.equals(array[i])) { - return true; - } - } - return false; - } - - /** - * Gets the config id from args[idx], or returns DEFAULT_CONFIG_ID if args[idx] does not exist. - * If justUseDefault, overrides and just uses DEFAULT_CONFIG_ID instead. - */ - private static long getConfigId(boolean justUseDefault, String[] args, int idx) - throws NumberFormatException { - if (justUseDefault || args.length <= idx || idx < 0) { - return DEFAULT_CONFIG_ID; - } - try { - return Long.valueOf(args[idx]); - } catch (NumberFormatException e) { - sLogger.severe("Bad config id provided: " + args[idx]); - throw e; - } - } -} diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java deleted file mode 100644 index 51bcad115cc5..000000000000 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * 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.statsd.shelltools.testdrive; - -import com.android.internal.os.StatsdConfigProto; -import com.android.internal.os.StatsdConfigProto.AtomMatcher; -import com.android.internal.os.StatsdConfigProto.EventMetric; -import com.android.internal.os.StatsdConfigProto.FieldFilter; -import com.android.internal.os.StatsdConfigProto.GaugeMetric; -import com.android.internal.os.StatsdConfigProto.PullAtomPackages; -import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher; -import com.android.internal.os.StatsdConfigProto.StatsdConfig; -import com.android.internal.os.StatsdConfigProto.TimeUnit; -import com.android.os.AtomsProto.Atom; -import com.android.os.StatsLog; -import com.android.os.StatsLog.ConfigMetricsReport; -import com.android.os.StatsLog.ConfigMetricsReportList; -import com.android.os.StatsLog.StatsLogReport; -import com.android.statsd.shelltools.Utils; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.io.Files; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class TestDrive { - - private static final int METRIC_ID_BASE = 1111; - private static final long ATOM_MATCHER_ID_BASE = 1234567; - private static final long APP_BREADCRUMB_MATCHER_ID = 1111111; - private static final int PULL_ATOM_START = 10000; - private static final int MAX_PLATFORM_ATOM_TAG = 100000; - private static final int VENDOR_PULLED_ATOM_START_TAG = 150000; - private static final long CONFIG_ID = 54321; - private static final String[] ALLOWED_LOG_SOURCES = { - "AID_GRAPHICS", - "AID_INCIDENTD", - "AID_STATSD", - "AID_RADIO", - "com.android.systemui", - "com.android.vending", - "AID_SYSTEM", - "AID_ROOT", - "AID_BLUETOOTH", - "AID_LMKD", - "com.android.managedprovisioning", - "AID_MEDIA", - "AID_NETWORK_STACK", - "com.google.android.providers.media.module", - }; - private static final String[] DEFAULT_PULL_SOURCES = { - "AID_SYSTEM", - "AID_RADIO" - }; - private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName()); - - @VisibleForTesting - String mDeviceSerial = null; - @VisibleForTesting - Dumper mDumper = new BasicDumper(); - - public static void main(String[] args) { - final Configuration configuration = new Configuration(); - final TestDrive testDrive = new TestDrive(); - Utils.setUpLogger(LOGGER, false); - - if (!testDrive.processArgs(configuration, args, - Utils.getDeviceSerials(LOGGER), Utils.getDefaultDevice(LOGGER))) { - return; - } - - final ConfigMetricsReportList reports = testDrive.testDriveAndGetReports( - configuration.createConfig(), configuration.hasPulledAtoms(), - configuration.hasPushedAtoms()); - if (reports != null) { - configuration.dumpMetrics(reports, testDrive.mDumper); - } - } - - boolean processArgs(Configuration configuration, String[] args, List<String> connectedDevices, - String defaultDevice) { - if (args.length < 1) { - LOGGER.severe("Usage: ./test_drive [-one] " - + "[-p additional_allowed_package] " - + "[-s DEVICE_SERIAL_NUMBER] " - + "<atomId1> <atomId2> ... <atomIdN>"); - return false; - } - - int first_arg = 0; - // Consume all flags, which must precede all atoms - for (; first_arg < args.length; ++first_arg) { - String arg = args[first_arg]; - int remaining_args = args.length - first_arg; - if (remaining_args >= 2 && arg.equals("-one")) { - LOGGER.info("Creating one event metric to catch all pushed atoms."); - configuration.mOnePushedAtomEvent = true; - } else if (remaining_args >= 2 && arg.equals("-terse")) { - LOGGER.info("Terse output format."); - mDumper = new TerseDumper(); - } else if (remaining_args >= 3 && arg.equals("-p")) { - configuration.mAdditionalAllowedPackage = args[++first_arg]; - } else if (remaining_args >= 3 && arg.equals("-s")) { - mDeviceSerial = args[++first_arg]; - } else { - break; // Found the atom list - } - } - - mDeviceSerial = Utils.chooseDevice(mDeviceSerial, connectedDevices, defaultDevice, LOGGER); - if (mDeviceSerial == null) { - return false; - } - - for ( ; first_arg < args.length; ++first_arg) { - String atom = args[first_arg]; - try { - configuration.addAtom(Integer.valueOf(atom)); - } catch (NumberFormatException e) { - LOGGER.severe("Bad atom id provided: " + atom); - } - } - - return configuration.hasPulledAtoms() || configuration.hasPushedAtoms(); - } - - private ConfigMetricsReportList testDriveAndGetReports(StatsdConfig config, - boolean hasPulledAtoms, boolean hasPushedAtoms) { - if (config == null) { - LOGGER.severe("Failed to create valid config."); - return null; - } - - String remoteConfigPath = null; - try { - remoteConfigPath = pushConfig(config, mDeviceSerial); - LOGGER.info("Pushed the following config to statsd on device '" + mDeviceSerial - + "':"); - LOGGER.info(config.toString()); - if (hasPushedAtoms) { - LOGGER.info("Now please play with the device to trigger the event."); - } - if (!hasPulledAtoms) { - LOGGER.info( - "All events should be dumped after 1 min ..."); - Thread.sleep(60_000); - } else { - LOGGER.info("All events should be dumped after 1.5 minutes ..."); - Thread.sleep(15_000); - Utils.logAppBreadcrumb(0, 0, LOGGER, mDeviceSerial); - Thread.sleep(75_000); - } - return Utils.getReportList(CONFIG_ID, true, false, LOGGER, - mDeviceSerial); - } catch (Exception e) { - LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e); - } finally { - removeConfig(mDeviceSerial); - if (remoteConfigPath != null) { - try { - Utils.runCommand(null, LOGGER, - "adb", "-s", mDeviceSerial, "shell", "rm", - remoteConfigPath); - } catch (Exception e) { - LOGGER.log(Level.WARNING, - "Unable to remove remote config file: " + remoteConfigPath, e); - } - } - } - return null; - } - - static class Configuration { - boolean mOnePushedAtomEvent = false; - @VisibleForTesting - Set<Integer> mPushedAtoms = new TreeSet<>(); - @VisibleForTesting - Set<Integer> mPulledAtoms = new TreeSet<>(); - @VisibleForTesting - String mAdditionalAllowedPackage = null; - private final Set<Long> mTrackedMetrics = new HashSet<>(); - - private void dumpMetrics(ConfigMetricsReportList reportList, Dumper dumper) { - // We may get multiple reports. Take the last one. - ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1); - for (StatsLogReport statsLog : report.getMetricsList()) { - if (isTrackedMetric(statsLog.getMetricId())) { - dumper.dump(statsLog); - } - } - } - - boolean isTrackedMetric(long metricId) { - return mTrackedMetrics.contains(metricId); - } - - static boolean isPulledAtom(int atomId) { - return atomId >= PULL_ATOM_START && atomId <= MAX_PLATFORM_ATOM_TAG - || atomId >= VENDOR_PULLED_ATOM_START_TAG; - } - - void addAtom(Integer atom) { - if (Atom.getDescriptor().findFieldByNumber(atom) == null) { - LOGGER.severe("No such atom found: " + atom); - return; - } - if (isPulledAtom(atom)) { - mPulledAtoms.add(atom); - } else { - mPushedAtoms.add(atom); - } - } - - private boolean hasPulledAtoms() { - return !mPulledAtoms.isEmpty(); - } - - private boolean hasPushedAtoms() { - return !mPushedAtoms.isEmpty(); - } - - StatsdConfig createConfig() { - long metricId = METRIC_ID_BASE; - long atomMatcherId = ATOM_MATCHER_ID_BASE; - - StatsdConfig.Builder builder = baseBuilder(); - - if (hasPulledAtoms()) { - builder.addAtomMatcher( - createAtomMatcher( - Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, - APP_BREADCRUMB_MATCHER_ID)); - } - - for (int atomId : mPulledAtoms) { - builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId)); - GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder(); - gaugeMetricBuilder - .setId(metricId) - .setWhat(atomMatcherId) - .setTriggerEvent(APP_BREADCRUMB_MATCHER_ID) - .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build()) - .setBucket(TimeUnit.ONE_MINUTE) - .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES) - .setMaxNumGaugeAtomsPerBucket(100); - builder.addGaugeMetric(gaugeMetricBuilder.build()); - atomMatcherId++; - mTrackedMetrics.add(metricId++); - } - - // A simple atom matcher for each pushed atom. - List<AtomMatcher> simpleAtomMatchers = new ArrayList<>(); - for (int atomId : mPushedAtoms) { - final AtomMatcher atomMatcher = createAtomMatcher(atomId, atomMatcherId++); - simpleAtomMatchers.add(atomMatcher); - builder.addAtomMatcher(atomMatcher); - } - - if (mOnePushedAtomEvent) { - // Create a union event metric, using an matcher that matches all pulled atoms. - AtomMatcher unionAtomMatcher = createUnionMatcher(simpleAtomMatchers, - atomMatcherId); - builder.addAtomMatcher(unionAtomMatcher); - EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder(); - eventMetricBuilder.setId(metricId).setWhat(unionAtomMatcher.getId()); - builder.addEventMetric(eventMetricBuilder.build()); - mTrackedMetrics.add(metricId++); - } else { - // Create multiple event metrics, one per pulled atom. - for (AtomMatcher atomMatcher : simpleAtomMatchers) { - EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder(); - eventMetricBuilder - .setId(metricId) - .setWhat(atomMatcher.getId()); - builder.addEventMetric(eventMetricBuilder.build()); - mTrackedMetrics.add(metricId++); - } - } - - return builder.build(); - } - - private static AtomMatcher createAtomMatcher(int atomId, long matcherId) { - AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder(); - atomMatcherBuilder - .setId(matcherId) - .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId)); - return atomMatcherBuilder.build(); - } - - private AtomMatcher createUnionMatcher(List<AtomMatcher> simpleAtomMatchers, - long atomMatcherId) { - AtomMatcher.Combination.Builder combinationBuilder = - AtomMatcher.Combination.newBuilder(); - combinationBuilder.setOperation(StatsdConfigProto.LogicalOperation.OR); - for (AtomMatcher matcher : simpleAtomMatchers) { - combinationBuilder.addMatcher(matcher.getId()); - } - AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder(); - atomMatcherBuilder.setId(atomMatcherId).setCombination(combinationBuilder.build()); - return atomMatcherBuilder.build(); - } - - private StatsdConfig.Builder baseBuilder() { - ArrayList<String> allowedSources = new ArrayList<>(); - Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES); - if (mAdditionalAllowedPackage != null) { - allowedSources.add(mAdditionalAllowedPackage); - } - return StatsdConfig.newBuilder() - .addAllAllowedLogSource(allowedSources) - .addAllDefaultPullPackages(Arrays.asList(DEFAULT_PULL_SOURCES)) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.GPU_STATS_GLOBAL_INFO_FIELD_NUMBER) - .addPackages("AID_GPU_SERVICE")) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.GPU_STATS_APP_INFO_FIELD_NUMBER) - .addPackages("AID_GPU_SERVICE")) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER) - .addPackages("AID_STATSD")) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.GENERAL_EXTERNAL_STORAGE_ACCESS_STATS_FIELD_NUMBER) - .addPackages("com.google.android.providers.media.module")) - .setHashStringsInMetricReport(false); - } - } - - interface Dumper { - void dump(StatsLogReport report); - } - - static class BasicDumper implements Dumper { - @Override - public void dump(StatsLogReport report) { - System.out.println(report.toString()); - } - } - - static class TerseDumper extends BasicDumper { - @Override - public void dump(StatsLogReport report) { - if (report.hasGaugeMetrics()) { - dumpGaugeMetrics(report); - } - if (report.hasEventMetrics()) { - dumpEventMetrics(report); - } - } - void dumpEventMetrics(StatsLogReport report) { - final List<StatsLog.EventMetricData> data = report.getEventMetrics().getDataList(); - if (data.isEmpty()) { - return; - } - long firstTimestampNanos = data.get(0).getElapsedTimestampNanos(); - for (StatsLog.EventMetricData event : data) { - final double deltaSec = (event.getElapsedTimestampNanos() - firstTimestampNanos) - / 1e9; - System.out.println( - String.format("+%.3fs: %s", deltaSec, event.getAtom().toString())); - } - } - void dumpGaugeMetrics(StatsLogReport report) { - final List<StatsLog.GaugeMetricData> data = report.getGaugeMetrics().getDataList(); - if (data.isEmpty()) { - return; - } - for (StatsLog.GaugeMetricData gauge : data) { - System.out.println(gauge.toString()); - } - } - } - - private static String pushConfig(StatsdConfig config, String deviceSerial) - throws IOException, InterruptedException { - File configFile = File.createTempFile("statsdconfig", ".config"); - configFile.deleteOnExit(); - Files.write(config.toByteArray(), configFile); - String remotePath = "/data/local/tmp/" + configFile.getName(); - Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial, - "push", configFile.getAbsolutePath(), remotePath); - Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial, - "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG, - String.valueOf(CONFIG_ID)); - return remotePath; - } - - private static void removeConfig(String deviceSerial) { - try { - Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial, - "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID)); - } catch (Exception e) { - LOGGER.severe("Failed to remove config: " + e.getMessage()); - } - } -} diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java deleted file mode 100644 index b1cc60f74993..000000000000 --- a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java +++ /dev/null @@ -1,326 +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.statsd.shelltools.testdrive; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import com.android.internal.os.StatsdConfigProto; -import com.android.internal.os.StatsdConfigProto.StatsdConfig; -import com.android.os.AtomsProto; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Tests for {@link TestDrive} - */ -public class ConfigurationTest { - - private StatsdConfigProto.AtomMatcher findAndRemoveAtomMatcherById( - List<StatsdConfigProto.AtomMatcher> atomMatchers, long id) { - int numMatches = 0; - StatsdConfigProto.AtomMatcher match = null; - for (StatsdConfigProto.AtomMatcher atomMatcher : atomMatchers) { - if (id == atomMatcher.getId()) { - ++numMatches; - match = atomMatcher; - } - } - if (numMatches == 1) { - atomMatchers.remove(match); - return match; - } - return null; // Too many, or not found - } - - private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration(); - - @Test - public void testOnePushed() { - final int atom = 90; - assertFalse(TestDrive.Configuration.isPulledAtom(atom)); - mConfiguration.addAtom(atom); - StatsdConfig config = mConfiguration.createConfig(); - - //event_metric { - // id: 1111 - // what: 1234567 - //} - //atom_matcher { - // id: 1234567 - // simple_atom_matcher { - // atom_id: 90 - // } - //} - - assertEquals(1, config.getEventMetricCount()); - assertEquals(0, config.getGaugeMetricCount()); - - assertTrue(mConfiguration.isTrackedMetric(config.getEventMetric(0).getId())); - - final List<StatsdConfigProto.AtomMatcher> atomMatchers = - new ArrayList<>(config.getAtomMatcherList()); - assertEquals(atom, - findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat()) - .getSimpleAtomMatcher().getAtomId()); - assertEquals(0, atomMatchers.size()); - } - - @Test - public void testOnePulled() { - final int atom = 10022; - assertTrue(TestDrive.Configuration.isPulledAtom(atom)); - mConfiguration.addAtom(atom); - StatsdConfig config = mConfiguration.createConfig(); - - //gauge_metric { - // id: 1111 - // what: 1234567 - // gauge_fields_filter { - // include_all: true - // } - // bucket: ONE_MINUTE - // sampling_type: FIRST_N_SAMPLES - // max_num_gauge_atoms_per_bucket: 100 - // trigger_event: 1111111 - //} - //atom_matcher { - // id: 1111111 - // simple_atom_matcher { - // atom_id: 47 - // } - //} - //atom_matcher { - // id: 1234567 - // simple_atom_matcher { - // atom_id: 10022 - // } - //} - - assertEquals(0, config.getEventMetricCount()); - assertEquals(1, config.getGaugeMetricCount()); - - assertTrue(mConfiguration.isTrackedMetric(config.getGaugeMetric(0).getId())); - - final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0); - assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll()); - - final List<StatsdConfigProto.AtomMatcher> atomMatchers = - new ArrayList<>(config.getAtomMatcherList()); - assertEquals(atom, - findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat()) - .getSimpleAtomMatcher().getAtomId()); - assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, - findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent()) - .getSimpleAtomMatcher().getAtomId()); - assertEquals(0, atomMatchers.size()); - } - - @Test - public void testOnePulledTwoPushed() { - final int pulledAtom = 10022; - assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom)); - mConfiguration.addAtom(pulledAtom); - - Integer[] pushedAtoms = new Integer[]{244, 245}; - for (int atom : pushedAtoms) { - assertFalse(TestDrive.Configuration.isPulledAtom(atom)); - mConfiguration.addAtom(atom); - } - StatsdConfig config = mConfiguration.createConfig(); - - // event_metric { - // id: 1111 - // what: 1234567 - // } - // event_metric { - // id: 1112 - // what: 1234568 - // } - // gauge_metric { - // id: 1114 - // what: 1234570 - // gauge_fields_filter { - // include_all: true - // } - // bucket: ONE_MINUTE - // sampling_type: FIRST_N_SAMPLES - // max_num_gauge_atoms_per_bucket: 100 - // trigger_event: 1111111 - // } - // atom_matcher { - // id: 1111111 - // simple_atom_matcher { - // atom_id: 47 - // } - // } - // atom_matcher { - // id: 1234567 - // simple_atom_matcher { - // atom_id: 244 - // } - // } - // atom_matcher { - // id: 1234568 - // simple_atom_matcher { - // atom_id: 245 - // } - // } - // atom_matcher { - // id: 1234570 - // simple_atom_matcher { - // atom_id: 10022 - // } - // } - - assertEquals(2, config.getEventMetricCount()); - assertEquals(1, config.getGaugeMetricCount()); - - final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0); - assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId())); - assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll()); - for (StatsdConfigProto.EventMetric eventMetric : config.getEventMetricList()) { - assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId())); - } - - final List<StatsdConfigProto.AtomMatcher> atomMatchers = - new ArrayList<>(config.getAtomMatcherList()); - - assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat()) - .getSimpleAtomMatcher().getAtomId()); - assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, - findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent()) - .getSimpleAtomMatcher().getAtomId()); - - Integer[] actualAtoms = new Integer[]{ - findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat()) - .getSimpleAtomMatcher().getAtomId(), - findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(1).getWhat()) - .getSimpleAtomMatcher().getAtomId()}; - Arrays.sort(actualAtoms); - assertArrayEquals(pushedAtoms, actualAtoms); - - assertEquals(0, atomMatchers.size()); - } - - @Test - public void testOnePulledTwoPushedTogether() { - mConfiguration.mOnePushedAtomEvent = true; // Use one event grabbing all pushed atoms - - final int pulledAtom = 10022; - assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom)); - mConfiguration.addAtom(pulledAtom); - - Integer[] pushedAtoms = new Integer[]{244, 245}; - for (int atom : pushedAtoms) { - assertFalse(TestDrive.Configuration.isPulledAtom(atom)); - mConfiguration.addAtom(atom); - } - StatsdConfig config = mConfiguration.createConfig(); - - // event_metric { - // id: 1112 - // what: 1234570 - // } - // gauge_metric { - // id: 1111 - // what: 1234567 - // gauge_fields_filter { - // include_all: true - // } - // bucket: ONE_MINUTE - // sampling_type: FIRST_N_SAMPLES - // max_num_gauge_atoms_per_bucket: 100 - // trigger_event: 1111111 - // } - // atom_matcher { - // id: 1111111 - // simple_atom_matcher { - // atom_id: 47 - // } - // } - // atom_matcher { - // id: 1234567 - // simple_atom_matcher { - // atom_id: 10022 - // } - // } - // atom_matcher { - // id: 1234568 - // simple_atom_matcher { - // atom_id: 244 - // } - // } - // atom_matcher { - // id: 1234569 - // simple_atom_matcher { - // atom_id: 245 - // } - // } - // atom_matcher { - // id: 1234570 - // combination { - // operation: OR - // matcher: 1234568 - // matcher: 1234569 - // } - // } - - assertEquals(1, config.getEventMetricCount()); - assertEquals(1, config.getGaugeMetricCount()); - - final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0); - assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId())); - assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll()); - - StatsdConfigProto.EventMetric eventMetric = config.getEventMetric(0); - assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId())); - - final List<StatsdConfigProto.AtomMatcher> atomMatchers = - new ArrayList<>(config.getAtomMatcherList()); - - assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat()) - .getSimpleAtomMatcher().getAtomId()); - assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, - findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent()) - .getSimpleAtomMatcher().getAtomId()); - - StatsdConfigProto.AtomMatcher unionMatcher = findAndRemoveAtomMatcherById(atomMatchers, - eventMetric.getWhat()); - assertNotNull(unionMatcher.getCombination()); - assertEquals(2, unionMatcher.getCombination().getMatcherCount()); - - Integer[] actualAtoms = new Integer[]{ - findAndRemoveAtomMatcherById(atomMatchers, - unionMatcher.getCombination().getMatcher(0)) - .getSimpleAtomMatcher().getAtomId(), - findAndRemoveAtomMatcherById(atomMatchers, - unionMatcher.getCombination().getMatcher(1)) - .getSimpleAtomMatcher().getAtomId()}; - Arrays.sort(actualAtoms); - assertArrayEquals(pushedAtoms, actualAtoms); - - assertEquals(0, atomMatchers.size()); - } -} diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java deleted file mode 100644 index 363fac0c78ba..000000000000 --- a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java +++ /dev/null @@ -1,195 +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.statsd.shelltools.testdrive; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Tests for {@link TestDrive} - */ -@RunWith(Parameterized.class) -public class TestDriveTest { - /** - * Expected results of a single iteration of the paramerized test. - */ - static class Expect { - public boolean success; - public Integer[] atoms; - public boolean onePushedAtomEvent = false; - public String extraPackage = null; - public String target; - public boolean terse = false; - - static Expect success(Integer... atoms) { - return new Expect(true, atoms, - TARGET); - } - Expect(boolean success, Integer[] atoms, String target) { - this.success = success; - this.atoms = atoms; - this.target = target; - } - static final Expect FAILURE = new Expect(false, null, null); - Expect onePushedAtomEvent() { - this.onePushedAtomEvent = true; - return this; - } - Expect extraPackage() { - this.extraPackage = TestDriveTest.PACKAGE; - return this; - } - Expect terse() { - this.terse = true; - return this; - } - } - - @Parameterized.Parameter(0) - public String[] mArgs; - - @Parameterized.Parameter(1) - public List<String> mConnectedDevices; - - @Parameterized.Parameter(2) - public String mDefaultDevice; - - @Parameterized.Parameter(3) - public Expect mExpect; - - private static final String TARGET = "target"; - private static final List<String> TARGET_AND_OTHER = Arrays.asList("otherDevice", - TARGET); - private static final List<String> TWO_OTHER_DEVICES = Arrays.asList( - "other1", "other2"); - private static final List<String> TARGET_ONLY = Collections.singletonList(TARGET); - private static final List<String> NOT_TARGET = Collections.singletonList("other"); - private static final List<String> NO_DEVICES = Collections.emptyList(); - private static final String PACKAGE = "extraPackage"; - - @Parameterized.Parameters - public static Collection<Object[]> data() { - return Arrays.asList( - new Object[]{new String[]{}, null, null, - Expect.FAILURE}, // Usage explanation - new Object[]{new String[]{"244", "245"}, null, null, - Expect.FAILURE}, // Failure looking up connected devices - new Object[]{new String[]{"244", "245"}, NO_DEVICES, null, - Expect.FAILURE}, // No connected devices - new Object[]{new String[]{"-s", TARGET, "244", "245"}, NOT_TARGET, null, - Expect.FAILURE}, // Wrong device connected - new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, null, - Expect.FAILURE}, // Wrong devices connected - new Object[]{new String[]{"244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245)}, // If only one device connected, guess that one - new Object[]{new String[]{"244", "not_an_atom"}, TARGET_ONLY, null, - Expect.success(244)}, // Ignore non-atoms - new Object[]{new String[]{"not_an_atom"}, TARGET_ONLY, null, - Expect.FAILURE}, // Require at least one atom - new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, TARGET, - Expect.FAILURE}, // ANDROID_SERIAL specifies non-connected target - new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, TARGET, - Expect.success(244, 245)}, // ANDROID_SERIAL specifies a valid target - new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, null, - Expect.FAILURE}, // Two connected devices, no indication of which to use - new Object[]{new String[]{"-one", "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).onePushedAtomEvent()}, - new Object[]{new String[]{"-terse", "-one", "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).onePushedAtomEvent().terse()}, - new Object[]{new String[]{"-one", "-terse", "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).onePushedAtomEvent().terse()}, - new Object[]{new String[]{"-p", PACKAGE, "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).extraPackage()}, - new Object[]{new String[]{"-p", PACKAGE, "-one", "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, - new Object[]{new String[]{"-one", "-p", PACKAGE, "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, - new Object[]{new String[]{"-s", TARGET, "-one", "-p", PACKAGE, "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, - new Object[]{new String[]{"-one", "-s", TARGET, "-p", PACKAGE, "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, - new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, - new Object[]{new String[]{"-terse", "-one", "-p", PACKAGE, "-s", TARGET, - "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}, - new Object[]{new String[]{"-one", "-terse", "-p", PACKAGE, "-s", TARGET, - "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}, - new Object[]{new String[]{"-one", "-p", PACKAGE, "-terse", "-s", TARGET, - "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}, - new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "-terse", - "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()} - ); - } - - private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration(); - private final TestDrive mTestDrive = new TestDrive(); - - private static Integer[] collectAtoms(TestDrive.Configuration configuration) { - Integer[] result = new Integer[configuration.mPulledAtoms.size() - + configuration.mPushedAtoms.size()]; - int result_index = 0; - for (Integer atom : configuration.mPushedAtoms) { - result[result_index++] = atom; - } - for (Integer atom : configuration.mPulledAtoms) { - result[result_index++] = atom; - } - Arrays.sort(result); - return result; - } - - @Test - public void testProcessArgs() { - boolean result = mTestDrive.processArgs(mConfiguration, mArgs, mConnectedDevices, - mDefaultDevice); - if (mExpect.success) { - assertTrue(result); - assertArrayEquals(mExpect.atoms, collectAtoms(mConfiguration)); - assertEquals(mExpect.onePushedAtomEvent, mConfiguration.mOnePushedAtomEvent); - assertEquals(mExpect.target, mTestDrive.mDeviceSerial); - if (mExpect.terse) { - assertEquals(TestDrive.TerseDumper.class, mTestDrive.mDumper.getClass()); - } else { - assertEquals(TestDrive.BasicDumper.class, mTestDrive.mDumper.getClass()); - } - } else { - assertFalse(result); - } - } -} diff --git a/cmds/statsd/tools/localtools/testdrive_manifest.txt b/cmds/statsd/tools/localtools/testdrive_manifest.txt deleted file mode 100644 index 625ebfa4312a..000000000000 --- a/cmds/statsd/tools/localtools/testdrive_manifest.txt +++ /dev/null @@ -1 +0,0 @@ -Main-class: com.android.statsd.shelltools.testdrive.TestDrive diff --git a/core/api/current.txt b/core/api/current.txt index b61bc2fde0e9..8b350f5b7c06 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -2883,6 +2883,7 @@ package android.accessibilityservice { method protected void onServiceConnected(); method public void onSystemActionsChanged(); method public final boolean performGlobalAction(int); + method public void setAccessibilityFocusAppearance(int, @ColorInt int); method public void setGestureDetectionPassthroughRegion(int, @NonNull android.graphics.Region); method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); method public void setTouchExplorationPassthroughRegion(int, @NonNull android.graphics.Region); @@ -7150,6 +7151,7 @@ package android.app.admin { field @RequiresPermission(android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY"; field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE"; field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE"; + field public static final String EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES = "android.app.extra.PROVISIONING_ALLOWED_PROVISIONING_MODES"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM"; @@ -7254,6 +7256,7 @@ package android.app.admin { field public static final int PRIVATE_DNS_SET_NO_ERROR = 0; // 0x0 field public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1; // 0x1 field public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; // 0x2 + field public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE = 3; // 0x3 field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2 field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1 field public static final int SKIP_SETUP_WIZARD = 1; // 0x1 @@ -7686,6 +7689,7 @@ package android.app.job { method public long getTriggerContentMaxDelay(); method public long getTriggerContentUpdateDelay(); method @Nullable public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris(); + method public boolean isForegroundJob(); method public boolean isImportantWhileForeground(); method public boolean isPeriodic(); method public boolean isPersisted(); @@ -7717,7 +7721,8 @@ package android.app.job { method public android.app.job.JobInfo.Builder setClipData(@Nullable android.content.ClipData, int); method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long); method public android.app.job.JobInfo.Builder setExtras(@NonNull android.os.PersistableBundle); - method public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean); + method @NonNull public android.app.job.JobInfo.Builder setForeground(boolean); + method @Deprecated public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean); method public android.app.job.JobInfo.Builder setMinimumLatency(long); method public android.app.job.JobInfo.Builder setOverrideDeadline(long); method public android.app.job.JobInfo.Builder setPeriodic(long); @@ -7757,6 +7762,7 @@ package android.app.job { method @NonNull public android.os.Bundle getTransientExtras(); method @Nullable public String[] getTriggeredContentAuthorities(); method @Nullable public android.net.Uri[] getTriggeredContentUris(); + method public boolean isForegroundJob(); method public boolean isOverrideDeadlineExpired(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.job.JobParameters> CREATOR; @@ -10328,7 +10334,7 @@ package android.content { method public int checkPermission(String, int, int); method public int checkSelfPermission(String); method public int checkUriPermission(android.net.Uri, int, int, int); - method public int checkUriPermission(android.net.Uri, String, String, int, int, int); + method public int checkUriPermission(@Nullable android.net.Uri, @Nullable String, @Nullable String, int, int, int); method @Deprecated public void clearWallpaper() throws java.io.IOException; method public android.content.Context createConfigurationContext(android.content.res.Configuration); method public android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException; @@ -10339,13 +10345,13 @@ package android.content { method public boolean deleteDatabase(String); method public boolean deleteFile(String); method public boolean deleteSharedPreferences(String); - method public void enforceCallingOrSelfPermission(String, String); + method public void enforceCallingOrSelfPermission(String, @Nullable String); method public void enforceCallingOrSelfUriPermission(android.net.Uri, int, String); - method public void enforceCallingPermission(String, String); + method public void enforceCallingPermission(String, @Nullable String); method public void enforceCallingUriPermission(android.net.Uri, int, String); - method public void enforcePermission(String, int, int, String); + method public void enforcePermission(String, int, int, @Nullable String); method public void enforceUriPermission(android.net.Uri, int, int, int, String); - method public void enforceUriPermission(android.net.Uri, String, String, int, int, int, String); + method public void enforceUriPermission(@Nullable android.net.Uri, @Nullable String, @Nullable String, int, int, int, @Nullable String); method public String[] fileList(); method public android.content.Context getApplicationContext(); method public android.content.pm.ApplicationInfo getApplicationInfo(); @@ -10358,9 +10364,9 @@ package android.content { method public java.io.File getDataDir(); method public java.io.File getDatabasePath(String); method public java.io.File getDir(String, int); - method public java.io.File getExternalCacheDir(); + method @Nullable public java.io.File getExternalCacheDir(); method public java.io.File[] getExternalCacheDirs(); - method public java.io.File getExternalFilesDir(String); + method @Nullable public java.io.File getExternalFilesDir(@Nullable String); method public java.io.File[] getExternalFilesDirs(String); method public java.io.File[] getExternalMediaDirs(); method public java.io.File getFileStreamPath(String); @@ -10388,40 +10394,40 @@ package android.content { method public java.io.FileInputStream openFileInput(String) throws java.io.FileNotFoundException; method public java.io.FileOutputStream openFileOutput(String, int) throws java.io.FileNotFoundException; method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory); - method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler); + method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, @Nullable android.database.DatabaseErrorHandler); method @Deprecated public android.graphics.drawable.Drawable peekWallpaper(); - method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter); - method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, int); - method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler); - method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler, int); + method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter); + method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, int); + method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler); + method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int); method @Deprecated public void removeStickyBroadcast(android.content.Intent); method @Deprecated public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void revokeUriPermission(android.net.Uri, int); method public void revokeUriPermission(String, android.net.Uri, int); method public void sendBroadcast(android.content.Intent); - method public void sendBroadcast(android.content.Intent, String); + method public void sendBroadcast(android.content.Intent, @Nullable String); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, String); - method public void sendOrderedBroadcast(android.content.Intent, String); - method public void sendOrderedBroadcast(android.content.Intent, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); + method public void sendOrderedBroadcast(android.content.Intent, @Nullable String); + method public void sendOrderedBroadcast(android.content.Intent, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method public void sendOrderedBroadcast(@NonNull @RequiresPermission android.content.Intent, int, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, @Nullable String, @Nullable android.os.Bundle, @Nullable android.os.Bundle); - method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); + method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method @Deprecated public void sendStickyBroadcast(android.content.Intent); method @Deprecated public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); - method @Deprecated public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); - method @Deprecated public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); + method @Deprecated public void sendStickyOrderedBroadcast(android.content.Intent, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); + method @Deprecated public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method public void setTheme(int); method @Deprecated public void setWallpaper(android.graphics.Bitmap) throws java.io.IOException; method @Deprecated public void setWallpaper(java.io.InputStream) throws java.io.IOException; method public void startActivities(android.content.Intent[]); - method public void startActivities(android.content.Intent[], android.os.Bundle); + method public void startActivities(android.content.Intent[], @Nullable android.os.Bundle); method public void startActivity(android.content.Intent); - method public void startActivity(android.content.Intent, android.os.Bundle); - method public android.content.ComponentName startForegroundService(android.content.Intent); - method public boolean startInstrumentation(android.content.ComponentName, String, android.os.Bundle); + method public void startActivity(android.content.Intent, @Nullable android.os.Bundle); + method @Nullable public android.content.ComponentName startForegroundService(android.content.Intent); + method public boolean startInstrumentation(android.content.ComponentName, @Nullable String, @Nullable android.os.Bundle); method public void startIntentSender(android.content.IntentSender, @Nullable android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, @Nullable android.content.Intent, int, int, int, @Nullable android.os.Bundle) throws android.content.IntentSender.SendIntentException; - method public android.content.ComponentName startService(android.content.Intent); + method @Nullable public android.content.ComponentName startService(android.content.Intent); method public boolean stopService(android.content.Intent); method public void unbindService(android.content.ServiceConnection); method public void unregisterReceiver(android.content.BroadcastReceiver); @@ -38699,6 +38705,25 @@ package android.provider { field public static final String UNGROUPED_WITH_PHONES = "summ_phones"; } + public static final class ContactsContract.SimAccount implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getAccountName(); + method @NonNull public String getAccountType(); + method public int getEfType(); + method public int getSimSlotIndex(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int ADN_EF_TYPE = 1; // 0x1 + field @NonNull public static final android.os.Parcelable.Creator<android.provider.ContactsContract.SimAccount> CREATOR; + field public static final int FDN_EF_TYPE = 3; // 0x3 + field public static final int SDN_EF_TYPE = 2; // 0x2 + field public static final int UNKNOWN_EF_TYPE = 0; // 0x0 + } + + public static final class ContactsContract.SimContacts { + method @NonNull public static java.util.List<android.provider.ContactsContract.SimAccount> getSimAccounts(@NonNull android.content.ContentResolver); + field public static final String ACTION_SIM_ACCOUNTS_CHANGED = "android.provider.action.SIM_ACCOUNTS_CHANGED"; + } + protected static interface ContactsContract.StatusColumns { field public static final int AVAILABLE = 5; // 0x5 field public static final int AWAY = 2; // 0x2 @@ -42438,6 +42463,7 @@ package android.service.notification { method public CharSequence getImportanceExplanation(); method public String getKey(); method public long getLastAudiblyAlertedMillis(); + method public int getLockscreenVisibilityOverride(); method public String getOverrideGroupKey(); method public int getRank(); method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions(); @@ -42451,6 +42477,7 @@ package android.service.notification { field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0 field public static final int USER_SENTIMENT_POSITIVE = 1; // 0x1 + field public static final int VISIBILITY_NO_OVERRIDE = -1000; // 0xfffffc18 } public static class NotificationListenerService.RankingMap implements android.os.Parcelable { @@ -44838,14 +44865,19 @@ package android.telecom { field public static final String EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME = "android.telecom.extra.DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME"; field public static final String EXTRA_DISCONNECT_CAUSE = "android.telecom.extra.DISCONNECT_CAUSE"; field public static final String EXTRA_HANDLE = "android.telecom.extra.HANDLE"; + field public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE"; field public static final String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS"; field public static final String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS"; + field public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE"; field public static final String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE"; field public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP"; + field public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION"; field public static final String EXTRA_NOTIFICATION_COUNT = "android.telecom.extra.NOTIFICATION_COUNT"; field public static final String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER"; field public static final String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS"; + field public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE"; field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE"; + field public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY"; field public static final String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT"; field public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE"; field public static final String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"; @@ -44861,6 +44893,8 @@ package android.telecom { field public static final int PRESENTATION_PAYPHONE = 4; // 0x4 field public static final int PRESENTATION_RESTRICTED = 2; // 0x2 field public static final int PRESENTATION_UNKNOWN = 3; // 0x3 + field public static final int PRIORITY_NORMAL = 0; // 0x0 + field public static final int PRIORITY_URGENT = 1; // 0x1 } public class VideoProfile implements android.os.Parcelable { @@ -45165,6 +45199,7 @@ package android.telephony { field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool"; field public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool"; field public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool"; + field public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING = "call_composer_picture_server_url_string"; field public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array"; field public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING = "call_redirection_service_component_name_string"; field public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL = "carrier_allow_deflect_ims_call_bool"; @@ -45354,6 +45389,7 @@ package android.telephony { field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; + field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool"; field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool"; field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool"; @@ -46248,6 +46284,12 @@ package android.telephony { field public static final int SCAN_TYPE_PERIODIC = 1; // 0x1 } + public final class PhoneCapability implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR; + } + public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher { ctor public PhoneNumberFormattingTextWatcher(); ctor public PhoneNumberFormattingTextWatcher(String); @@ -46316,10 +46358,10 @@ package android.telephony { public class PhoneStateListener { ctor public PhoneStateListener(); - ctor public PhoneStateListener(@NonNull java.util.concurrent.Executor); + ctor @Deprecated public PhoneStateListener(@NonNull java.util.concurrent.Executor); method public void onActiveDataSubscriptionIdChanged(int); method public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo); - method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int); method public void onCallForwardingIndicatorChanged(boolean); method public void onCallStateChanged(int, String); method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>); @@ -46327,36 +46369,140 @@ package android.telephony { method public void onDataActivity(int); method public void onDataConnectionStateChanged(int); method public void onDataConnectionStateChanged(int, int); - method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); method public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>); - method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); method public void onMessageWaitingIndicatorChanged(boolean); - method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); method public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); method public void onServiceStateChanged(android.telephony.ServiceState); method @Deprecated public void onSignalStrengthChanged(int); method public void onSignalStrengthsChanged(android.telephony.SignalStrength); method public void onUserMobileDataStateChanged(boolean); - field public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000 - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 - field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8 - field public static final int LISTEN_CALL_STATE = 32; // 0x20 - field public static final int LISTEN_CELL_INFO = 1024; // 0x400 - field public static final int LISTEN_CELL_LOCATION = 16; // 0x10 - field public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80 - field public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40 - field public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000 - field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000 - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 - field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4 + field @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 + field @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8 + field @Deprecated public static final int LISTEN_CALL_STATE = 32; // 0x20 + field @Deprecated public static final int LISTEN_CELL_INFO = 1024; // 0x400 + field @Deprecated public static final int LISTEN_CELL_LOCATION = 16; // 0x10 + field @Deprecated public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80 + field @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40 + field @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000 + field @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 + field @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4 field public static final int LISTEN_NONE = 0; // 0x0 - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000 - field public static final int LISTEN_SERVICE_STATE = 1; // 0x1 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000 + field @Deprecated public static final int LISTEN_SERVICE_STATE = 1; // 0x1 field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2 - field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100 - field public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000 + field @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100 + field @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000 + } + + public static interface PhoneStateListener.ActiveDataSubscriptionIdChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int); + } + + public static interface PhoneStateListener.AlwaysReportedSignalStrengthChangedListener { + method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength); + } + + public static interface PhoneStateListener.BarringInfoChangedListener { + method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo); + } + + public static interface PhoneStateListener.CallDisconnectCauseChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int); + } + + public static interface PhoneStateListener.CallForwardingIndicatorChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean); + } + + public static interface PhoneStateListener.CallStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String); + } + + public static interface PhoneStateListener.CarrierNetworkChangeListener { + method public void onCarrierNetworkChange(boolean); + } + + public static interface PhoneStateListener.CellInfoChangedListener { + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>); + } + + public static interface PhoneStateListener.CellLocationChangedListener { + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation); + } + + public static interface PhoneStateListener.DataActivationStateChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int); + } + + public static interface PhoneStateListener.DataActivityListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int); + } + + public static interface PhoneStateListener.DataConnectionStateChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int); + } + + public static interface PhoneStateListener.DisplayInfoChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); + } + + public static interface PhoneStateListener.EmergencyNumberListChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>); + } + + public static interface PhoneStateListener.ImsCallDisconnectCauseChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); + } + + public static interface PhoneStateListener.MessageWaitingIndicatorChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean); + } + + public static interface PhoneStateListener.PhoneCapabilityChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability); + } + + public static interface PhoneStateListener.PreciseDataConnectionStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); + } + + public static interface PhoneStateListener.RegistrationFailedListener { + method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); + } + + public static interface PhoneStateListener.ServiceStateChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState); + } + + public static interface PhoneStateListener.SignalStrengthsChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength); + } + + public static interface PhoneStateListener.UserMobileDataStateChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean); + } + + public final class PhysicalChannelConfig implements android.os.Parcelable { + method public int describeContents(); + method public int getCellBandwidthDownlink(); + method public int getChannelNumber(); + method public int getConnectionStatus(); + method public int getNetworkType(); + method @IntRange(from=0, to=1007) public int getPhysicalCellId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff + field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1 + field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2 + field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR; + field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff } public final class PreciseDataConnectionState implements android.os.Parcelable { @@ -46868,7 +47014,8 @@ package android.telephony { method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle); method public boolean isWorldPhone(); method @Deprecated public void listen(android.telephony.PhoneStateListener, int); - method public void listen(long, @NonNull android.telephony.PhoneStateListener); + method @Deprecated public void listen(long, @NonNull android.telephony.PhoneStateListener); + method public void registerPhoneStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.PhoneStateListener); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); method public void sendDialerSpecialCode(String); @@ -46890,6 +47037,7 @@ package android.telephony { method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri); method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int); + method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener); method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>); field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE"; field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; @@ -47671,6 +47819,7 @@ package android.telephony.ims.feature { public static class MmTelFeature.MmTelCapabilities { method public final boolean isCapable(int); + field public static final int CAPABILITY_TYPE_CALL_COMPOSER = 16; // 0x10 field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8 field public static final int CAPABILITY_TYPE_UT = 4; // 0x4 field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2 @@ -50868,7 +51017,7 @@ package android.view { method public int getFlags(); method @Nullable public android.net.Uri getLinkUri(); method public int getSource(); - method @NonNull public java.util.Map<java.lang.Boolean,android.view.ContentInfo> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>); + method @NonNull public android.util.Pair<android.view.ContentInfo,android.view.ContentInfo> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>); field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 field public static final int SOURCE_APP = 0; // 0x0 field public static final int SOURCE_AUTOFILL = 4; // 0x4 @@ -54586,6 +54735,8 @@ package android.view.accessibility { method public void addAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, @Nullable android.os.Handler); method public boolean addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener); method public void addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, @Nullable android.os.Handler); + method @ColorInt public int getAccessibilityFocusColor(); + method public int getAccessibilityFocusStrokeWidth(); method @Deprecated public java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList(); method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int); method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(); @@ -56539,6 +56690,7 @@ package android.view.textclassifier { method @Nullable public String getId(); method public int getSelectionEndIndex(); method public int getSelectionStartIndex(); + method @Nullable public android.view.textclassifier.TextClassification getTextClassification(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextSelection> CREATOR; } @@ -56549,6 +56701,7 @@ package android.view.textclassifier { method @NonNull public android.view.textclassifier.TextSelection.Builder setEntityType(@NonNull String, @FloatRange(from=0.0, to=1.0) float); method @NonNull public android.view.textclassifier.TextSelection.Builder setExtras(@Nullable android.os.Bundle); method @NonNull public android.view.textclassifier.TextSelection.Builder setId(@Nullable String); + method @NonNull public android.view.textclassifier.TextSelection.Builder setTextClassification(@Nullable android.view.textclassifier.TextClassification); } public static final class TextSelection.Request implements android.os.Parcelable { @@ -56559,6 +56712,7 @@ package android.view.textclassifier { method @NonNull public android.os.Bundle getExtras(); method @IntRange(from=0) public int getStartIndex(); method @NonNull public CharSequence getText(); + method public boolean shouldIncludeTextClassification(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextSelection.Request> CREATOR; } @@ -56568,6 +56722,7 @@ package android.view.textclassifier { method @NonNull public android.view.textclassifier.TextSelection.Request build(); method @NonNull public android.view.textclassifier.TextSelection.Request.Builder setDefaultLocales(@Nullable android.os.LocaleList); method @NonNull public android.view.textclassifier.TextSelection.Request.Builder setExtras(@Nullable android.os.Bundle); + method @NonNull public android.view.textclassifier.TextSelection.Request.Builder setIncludeTextClassification(boolean); } } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a5f2b6ac3af8..c3bf05813b90 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -878,11 +878,13 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL"; field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME"; + field public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = "android.app.extra.PROVISIONING_SUPPORTED_MODES"; field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL"; field public static final String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER"; field public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION"; field public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1 - field public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3 + field public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; // 0x4 + field @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3 field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2 field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0 field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4 @@ -891,6 +893,9 @@ package android.app.admin { field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3 field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1 field public static final int STATE_USER_UNMANAGED = 0; // 0x0 + field public static final int SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED = 3; // 0x3 + field public static final int SUPPORTED_MODES_ORGANIZATION_OWNED = 1; // 0x1 + field public static final int SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2 } public final class SystemUpdatePolicy implements android.os.Parcelable { @@ -1795,11 +1800,11 @@ package android.content { public class ContextWrapper extends android.content.Context { method public android.content.Context createCredentialProtectedStorageContext(); - method public java.io.File getPreloadsFileCache(); + method @Nullable public java.io.File getPreloadsFileCache(); method public boolean isCredentialProtectedStorage(); - method public void sendBroadcast(android.content.Intent, String, android.os.Bundle); - method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, String, android.os.Bundle); - method public void sendOrderedBroadcast(android.content.Intent, String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); + method public void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle); + method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle); + method public void sendOrderedBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); } public class Intent implements java.lang.Cloneable android.os.Parcelable { @@ -8383,6 +8388,11 @@ package android.provider { field @Deprecated public static final String STATE = "state"; } + public static final class ContactsContract.SimContacts { + method @RequiresPermission("android.contacts.permission.MANAGE_SIM_ACCOUNTS") public static void addSimAccount(@NonNull android.content.ContentResolver, @NonNull String, @NonNull String, int, int); + method @RequiresPermission("android.contacts.permission.MANAGE_SIM_ACCOUNTS") public static void removeSimAccounts(@NonNull android.content.ContentResolver, int); + } + public final class DeviceConfig { method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener); method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean); @@ -8401,6 +8411,7 @@ package android.provider { field public static final String NAMESPACE_APP_COMPAT = "app_compat"; field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service"; field public static final String NAMESPACE_AUTOFILL = "autofill"; + field public static final String NAMESPACE_BATTERY_SAVER = "battery_saver"; field public static final String NAMESPACE_BIOMETRICS = "biometrics"; field public static final String NAMESPACE_BLOBSTORE = "blobstore"; field public static final String NAMESPACE_BLUETOOTH = "bluetooth"; @@ -9088,6 +9099,26 @@ package android.service.carrier { method @NonNull @WorkerThread public abstract java.util.List<android.content.ContentValues> onRestoreApns(int); } + public final class CarrierMessagingServiceWrapper { + ctor public CarrierMessagingServiceWrapper(); + method public boolean bindToCarrierMessagingService(@NonNull android.content.Context, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull Runnable); + method public void disposeConnection(@NonNull android.content.Context); + method public void downloadMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback); + method public void receiveSms(@NonNull android.service.carrier.MessagePdu, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback); + method public void sendDataSms(@NonNull byte[], int, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback); + method public void sendMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback); + method public void sendMultipartTextSms(@NonNull java.util.List<java.lang.String>, int, @NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback); + method public void sendTextSms(@NonNull String, int, @NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback); + } + + public static interface CarrierMessagingServiceWrapper.CarrierMessagingCallback { + method public default void onDownloadMmsComplete(int); + method public default void onReceiveSmsComplete(int); + method public default void onSendMmsComplete(int, @Nullable byte[]); + method public default void onSendMultipartSmsComplete(int, @Nullable int[]); + method public default void onSendSmsComplete(int, int); + } + } package android.service.contentcapture { @@ -9708,6 +9739,33 @@ package android.telecom { field @Deprecated public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5 } + public final class BluetoothCallQualityReport implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0) public int getNegativeAcknowledgementCount(); + method @IntRange(from=0) public int getPacketsNotReceivedCount(); + method @IntRange(from=0) public int getRetransmittedPacketsCount(); + method @IntRange(from=0xffffff81, to=20) public int getRssiDbm(); + method public long getSentTimestampMillis(); + method public int getSnrDb(); + method public boolean isChoppyVoice(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telecom.BluetoothCallQualityReport> CREATOR; + field public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT = "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT"; + field public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT = "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT"; + } + + public static final class BluetoothCallQualityReport.Builder { + ctor public BluetoothCallQualityReport.Builder(); + method @NonNull public android.telecom.BluetoothCallQualityReport build(); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setChoppyVoice(boolean); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setNegativeAcknowledgementCount(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setPacketsNotReceivedCount(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setRetransmittedPacketsCount(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setRssiDbm(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setSentTimestampMillis(long); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setSnrDb(int); + } + public final class Call { method @Deprecated public void addListener(android.telecom.Call.Listener); method public void enterBackgroundAudioProcessing(); @@ -10375,35 +10433,87 @@ package android.telephony { method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber); method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); - method public void onPhysicalChannelConfigurationChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>); - method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); method public void onRadioPowerStateChanged(int); method public void onSrvccStateChanged(int); method public void onVoiceActivationStateChanged(int); - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 - field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000 - field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 4294967296L; // 0x100000000L - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 - } - - public final class PhysicalChannelConfig implements android.os.Parcelable { - method public int describeContents(); - method public int getCellBandwidthDownlink(); - method public int getChannelNumber(); - method public int getConnectionStatus(); - method public int getNetworkType(); - method @IntRange(from=0, to=1007) public int getPhysicalCellId(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff - field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1 - field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2 - field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR; - field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17 + field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa + field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4 + field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6 + field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11 + field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb + field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5 + field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13 + field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe + field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22 + field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15 + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf + field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d + field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e + field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18 + field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f + field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1 + field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9 + field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10 + field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 + } + + public static interface PhoneStateListener.CallAttributesChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); + } + + public static interface PhoneStateListener.DataEnabledChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int); + } + + public static interface PhoneStateListener.OutgoingEmergencyCallListener { + method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); + } + + public static interface PhoneStateListener.OutgoingEmergencySmsListener { + method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); + } + + public static interface PhoneStateListener.PhysicalChannelConfigChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>); + } + + public static interface PhoneStateListener.PreciseCallStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); + } + + public static interface PhoneStateListener.RadioPowerStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int); + } + + public static interface PhoneStateListener.SrvccStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int); + } + + public static interface PhoneStateListener.VoiceActivationStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int); } public final class PinResult implements android.os.Parcelable { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 2394919d4e1a..a448435926d9 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -18,6 +18,7 @@ package android { field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES"; field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS"; field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; + field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE"; field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS"; field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK"; field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS"; @@ -750,9 +751,14 @@ package android.hardware.display { } public final class DisplayManager { + method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public int getRefreshRateSwitchingType(); method public boolean isMinimalPostProcessingRequested(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int); method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean); method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode(); + field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2 + field public static final int SWITCHING_TYPE_NONE = 0; // 0x0 + field public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; // 0x1 field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200 field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400 } @@ -988,9 +994,9 @@ package android.media { } public class AudioSystem { - method public static float getMasterBalance(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static float getMasterBalance(); method public static final int getNumStreamTypes(); - method public static int setMasterBalance(float); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static int setMasterBalance(float); field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2 field public static final int DEVICE_ROLE_NONE = 0; // 0x0 field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1 diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 0ad9e446dfc7..8e50184c96e0 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -18,6 +18,7 @@ package android.accessibilityservice; import android.accessibilityservice.GestureDescription.MotionEventGenerator; import android.annotation.CallbackExecutor; +import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -2118,6 +2119,27 @@ public abstract class AccessibilityService extends Service { } /** + * Sets the strokeWidth and color of the accessibility focus rectangle. + * + * @param strokeWidth The stroke width of the rectangle in pixels. + * Setting this value to zero results in no focus rectangle being drawn. + * @param color The color of the rectangle. + */ + public void setAccessibilityFocusAppearance(int strokeWidth, @ColorInt int color) { + IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + if (connection != null) { + try { + connection.setFocusAppearance(strokeWidth, color); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while setting the strokeWidth and color of the " + + "accessibility focus rectangle", re); + re.rethrowFromSystemServer(); + } + } + } + + /** * Implement to return the implementation of the internal accessibility * service interface. */ diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 0b3b9b2ecae1..ab21dc9f14ad 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -115,4 +115,6 @@ interface IAccessibilityServiceConnection { void setGestureDetectionPassthroughRegion(int displayId, in Region region); void setTouchExplorationPassthroughRegion(int displayId, in Region region); + + void setFocusAppearance(int strokeWidth, int color); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 5ee5597e1984..f92768a28db1 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -963,8 +963,8 @@ public class Activity extends ContextThemeWrapper * @hide */ @Override - public void toggleFreeformWindowingMode() throws RemoteException { - ActivityTaskManager.getService().toggleFreeformWindowingMode(mToken); + public void toggleFreeformWindowingMode() { + ActivityClient.getInstance().toggleFreeformWindowingMode(mToken); } /** @@ -981,11 +981,8 @@ public class Activity extends ContextThemeWrapper @Override public boolean isTaskRoot() { - try { - return ActivityTaskManager.getService().getTaskForActivity(mToken, true) >= 0; - } catch (RemoteException e) { - return false; - } + return ActivityClient.getInstance().getTaskForActivity( + mToken, true /* onlyRoot */) >= 0; } /** @@ -2052,12 +2049,8 @@ public class Activity extends ContextThemeWrapper * {@link #isVoiceInteractionRoot()} return {@code false} in this case. */ public boolean isVoiceInteractionRoot() { - try { - return mVoiceInteractor != null - && ActivityTaskManager.getService().isRootVoiceInteraction(mToken); - } catch (RemoteException e) { - } - return false; + return mVoiceInteractor != null + && ActivityClient.getInstance().isRootVoiceInteraction(mToken); } /** @@ -2090,10 +2083,7 @@ public class Activity extends ContextThemeWrapper * @param privateOptions a Bundle of private arguments to the current voice interaction service */ public void startLocalVoiceInteraction(Bundle privateOptions) { - try { - ActivityTaskManager.getService().startLocalVoiceInteraction(mToken, privateOptions); - } catch (RemoteException re) { - } + ActivityClient.getInstance().startLocalVoiceInteraction(mToken, privateOptions); } /** @@ -2119,10 +2109,7 @@ public class Activity extends ContextThemeWrapper * terminated, {@link #onLocalVoiceInteractionStopped()} will be called. */ public void stopLocalVoiceInteraction() { - try { - ActivityTaskManager.getService().stopLocalVoiceInteraction(mToken); - } catch (RemoteException re) { - } + ActivityClient.getInstance().stopLocalVoiceInteraction(mToken); } /** @@ -2564,11 +2551,7 @@ public class Activity extends ContextThemeWrapper * false will be returned if the caller is not the current top activity. */ public boolean showAssist(Bundle args) { - try { - return ActivityTaskManager.getService().showAssistFromActivity(mToken, args); - } catch (RemoteException e) { - } - return false; + return ActivityClient.getInstance().showAssistFromActivity(mToken, args); } /** @@ -2708,10 +2691,9 @@ public class Activity extends ContextThemeWrapper } mDoReportFullyDrawn = false; try { - ActivityTaskManager.getService().reportActivityFullyDrawn( + ActivityClient.getInstance().reportActivityFullyDrawn( mToken, mRestoredFromBundle); VMRuntime.getRuntime().notifyStartupCompleted(); - } catch (RemoteException e) { } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } @@ -2841,27 +2823,23 @@ public class Activity extends ContextThemeWrapper * does not support picture-in-picture, return false. */ public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) { - try { - if (!deviceSupportsPictureInPictureMode()) { - return false; - } - if (params == null) { - throw new IllegalArgumentException("Expected non-null picture-in-picture params"); - } - if (!mCanEnterPictureInPicture) { - throw new IllegalStateException("Activity must be resumed to enter" - + " picture-in-picture"); - } - // Set mIsInPictureInPictureMode earlier and don't wait for - // onPictureInPictureModeChanged callback here. This is to ensure that - // isInPictureInPictureMode returns true in the following onPause callback. - // See https://developer.android.com/guide/topics/ui/picture-in-picture for guidance. - mIsInPictureInPictureMode = ActivityTaskManager.getService().enterPictureInPictureMode( - mToken, params); - return mIsInPictureInPictureMode; - } catch (RemoteException e) { + if (!deviceSupportsPictureInPictureMode()) { return false; } + if (params == null) { + throw new IllegalArgumentException("Expected non-null picture-in-picture params"); + } + if (!mCanEnterPictureInPicture) { + throw new IllegalStateException("Activity must be resumed to enter" + + " picture-in-picture"); + } + // Set mIsInPictureInPictureMode earlier and don't wait for + // onPictureInPictureModeChanged callback here. This is to ensure that + // isInPictureInPictureMode returns true in the following onPause callback. + // See https://developer.android.com/guide/topics/ui/picture-in-picture for guidance. + mIsInPictureInPictureMode = ActivityClient.getInstance().enterPictureInPictureMode( + mToken, params); + return mIsInPictureInPictureMode; } /** @@ -2871,16 +2849,13 @@ public class Activity extends ContextThemeWrapper * @param params the new parameters for the picture-in-picture. */ public void setPictureInPictureParams(@NonNull PictureInPictureParams params) { - try { - if (!deviceSupportsPictureInPictureMode()) { - return; - } - if (params == null) { - throw new IllegalArgumentException("Expected non-null picture-in-picture params"); - } - ActivityTaskManager.getService().setPictureInPictureParams(mToken, params); - } catch (RemoteException e) { + if (!deviceSupportsPictureInPictureMode()) { + return; } + if (params == null) { + throw new IllegalArgumentException("Expected non-null picture-in-picture params"); + } + ActivityClient.getInstance().setPictureInPictureParams(mToken, params); } /** @@ -3822,14 +3797,10 @@ public class Activity extends ContextThemeWrapper finishAfterTransition(); return; } - try { - // Inform activity task manager that the activity received a back press - // while at the root of the task. This call allows ActivityTaskManager - // to intercept or move the task to the back. - ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken); - } catch (RemoteException e) { - finishAfterTransition(); - } + // Inform activity task manager that the activity received a back press while at the + // root of the task. This call allows ActivityTaskManager to intercept or move the task + // to the back. + ActivityClient.getInstance().onBackPressedOnTaskRoot(mToken); // Activity was launched when user tapped a link in the Autofill Save UI - Save UI must // be restored now. @@ -6114,11 +6085,8 @@ public class Activity extends ContextThemeWrapper * the outgoing activity. Use 0 for no animation. */ public void overridePendingTransition(int enterAnim, int exitAnim) { - try { - ActivityTaskManager.getService().overridePendingTransition( - mToken, getPackageName(), enterAnim, exitAnim); - } catch (RemoteException e) { - } + ActivityClient.getInstance().overridePendingTransition(mToken, getPackageName(), + enterAnim, exitAnim); } /** @@ -6239,11 +6207,7 @@ public class Activity extends ContextThemeWrapper */ @Nullable public String getCallingPackage() { - try { - return ActivityTaskManager.getService().getCallingPackage(mToken); - } catch (RemoteException e) { - return null; - } + return ActivityClient.getInstance().getCallingPackage(mToken); } /** @@ -6262,11 +6226,7 @@ public class Activity extends ContextThemeWrapper */ @Nullable public ComponentName getCallingActivity() { - try { - return ActivityTaskManager.getService().getCallingActivity(mToken); - } catch (RemoteException e) { - return null; - } + return ActivityClient.getInstance().getCallingActivity(mToken); } /** @@ -6364,16 +6324,12 @@ public class Activity extends ContextThemeWrapper resultData = mResultData; } if (false) Log.v(TAG, "Finishing self: token=" + mToken); - try { - if (resultData != null) { - resultData.prepareToLeaveProcess(this); - } - if (ActivityTaskManager.getService() - .finishActivity(mToken, resultCode, resultData, finishTask)) { - mFinished = true; - } - } catch (RemoteException e) { - // Empty + if (resultData != null) { + resultData.prepareToLeaveProcess(this); + } + if (ActivityClient.getInstance().finishActivity(mToken, resultCode, resultData, + finishTask)) { + mFinished = true; } } else { mParent.finishFromChild(this); @@ -6429,12 +6385,8 @@ public class Activity extends ContextThemeWrapper if (mResultCode != RESULT_CANCELED || mResultData != null) { throw new IllegalStateException("Can not be called to deliver a result"); } - try { - if (ActivityTaskManager.getService().finishActivityAffinity(mToken)) { - mFinished = true; - } - } catch (RemoteException e) { - // Empty + if (ActivityClient.getInstance().finishActivityAffinity(mToken)) { + mFinished = true; } } @@ -6477,12 +6429,7 @@ public class Activity extends ContextThemeWrapper */ public void finishActivity(int requestCode) { if (mParent == null) { - try { - ActivityTaskManager.getService() - .finishSubActivity(mToken, mEmbeddedID, requestCode); - } catch (RemoteException e) { - // Empty - } + ActivityClient.getInstance().finishSubActivity(mToken, mEmbeddedID, requestCode); } else { mParent.finishActivityFromChild(this, requestCode); } @@ -6499,12 +6446,7 @@ public class Activity extends ContextThemeWrapper */ @Deprecated public void finishActivityFromChild(@NonNull Activity child, int requestCode) { - try { - ActivityTaskManager.getService() - .finishSubActivity(mToken, child.mEmbeddedID, requestCode); - } catch (RemoteException e) { - // Empty - } + ActivityClient.getInstance().finishSubActivity(mToken, child.mEmbeddedID, requestCode); } /** @@ -6527,12 +6469,7 @@ public class Activity extends ContextThemeWrapper * being finished, it hasn't yet saved its state, etc. */ public boolean releaseInstance() { - try { - return ActivityTaskManager.getService().releaseActivityInstance(mToken); - } catch (RemoteException e) { - // Empty - } - return false; + return ActivityClient.getInstance().releaseActivityInstance(mToken); } /** @@ -6644,12 +6581,7 @@ public class Activity extends ContextThemeWrapper */ public void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) { if (mParent == null) { - try { - ActivityTaskManager.getService().setRequestedOrientation( - mToken, requestedOrientation); - } catch (RemoteException e) { - // Empty - } + ActivityClient.getInstance().setRequestedOrientation(mToken, requestedOrientation); } else { mParent.setRequestedOrientation(requestedOrientation); } @@ -6667,16 +6599,10 @@ public class Activity extends ContextThemeWrapper @ActivityInfo.ScreenOrientation public int getRequestedOrientation() { if (mParent == null) { - try { - return ActivityTaskManager.getService() - .getRequestedOrientation(mToken); - } catch (RemoteException e) { - // Empty - } + return ActivityClient.getInstance().getRequestedOrientation(mToken); } else { return mParent.getRequestedOrientation(); } - return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } /** @@ -6686,11 +6612,7 @@ public class Activity extends ContextThemeWrapper * @return Task identifier, an opaque integer. */ public int getTaskId() { - try { - return ActivityTaskManager.getService().getTaskForActivity(mToken, false); - } catch (RemoteException e) { - return -1; - } + return ActivityClient.getInstance().getTaskForActivity(mToken, false /* onlyRoot */); } /** @@ -6715,12 +6637,7 @@ public class Activity extends ContextThemeWrapper * back) true is returned, else false. */ public boolean moveTaskToBack(boolean nonRoot) { - try { - return ActivityTaskManager.getService().moveActivityTaskToBack(mToken, nonRoot); - } catch (RemoteException e) { - // Empty - } - return false; + return ActivityClient.getInstance().moveActivityTaskToBack(mToken, nonRoot); } /** @@ -6896,10 +6813,7 @@ public class Activity extends ContextThemeWrapper mTaskDescription.setIcon(Icon.createWithBitmap(icon)); } } - try { - ActivityTaskManager.getService().setTaskDescription(mToken, mTaskDescription); - } catch (RemoteException e) { - } + ActivityClient.getInstance().setTaskDescription(mToken, mTaskDescription); } /** @@ -7210,11 +7124,7 @@ public class Activity extends ContextThemeWrapper * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE */ public boolean isImmersive() { - try { - return ActivityTaskManager.getService().isImmersive(mToken); - } catch (RemoteException e) { - return false; - } + return ActivityClient.getInstance().isImmersive(mToken); } /** @@ -7228,11 +7138,7 @@ public class Activity extends ContextThemeWrapper if (mToken == null || mWindow == null) { return false; } - try { - return ActivityTaskManager.getService().isTopOfTask(getActivityToken()); - } catch (RemoteException e) { - return false; - } + return ActivityClient.getInstance().isTopOfTask(getActivityToken()); } /** @@ -7271,14 +7177,10 @@ public class Activity extends ContextThemeWrapper } private boolean convertFromTranslucentInternal() { - try { - mTranslucentCallback = null; - if (ActivityTaskManager.getService().convertFromTranslucent(mToken)) { - WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, true); - return true; - } - } catch (RemoteException e) { - // pass + mTranslucentCallback = null; + if (ActivityClient.getInstance().convertFromTranslucent(mToken)) { + WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, true /* opaque */); + return true; } return false; } @@ -7307,21 +7209,14 @@ public class Activity extends ContextThemeWrapper @SystemApi public boolean convertToTranslucent(TranslucentConversionListener callback, ActivityOptions options) { - boolean drawComplete; - try { - mTranslucentCallback = callback; - mChangeCanvasToTranslucent = ActivityTaskManager.getService().convertToTranslucent( - mToken, options == null ? null : options.toBundle()); - WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, false); - drawComplete = true; - } catch (RemoteException e) { - // Make callback return as though it timed out. - mChangeCanvasToTranslucent = false; - drawComplete = false; - } + mTranslucentCallback = callback; + mChangeCanvasToTranslucent = ActivityClient.getInstance().convertToTranslucent( + mToken, options == null ? null : options.toBundle()); + WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, false); + if (!mChangeCanvasToTranslucent && mTranslucentCallback != null) { // Window is already translucent. - mTranslucentCallback.onTranslucentConversionComplete(drawComplete); + mTranslucentCallback.onTranslucentConversionComplete(true /* drawComplete */); } return mChangeCanvasToTranslucent; } @@ -7355,12 +7250,7 @@ public class Activity extends ContextThemeWrapper */ @UnsupportedAppUsage ActivityOptions getActivityOptions() { - try { - return ActivityOptions.fromBundle( - ActivityTaskManager.getService().getActivityOptions(mToken)); - } catch (RemoteException e) { - } - return null; + return ActivityOptions.fromBundle(ActivityClient.getInstance().getActivityOptions(mToken)); } /** @@ -7504,11 +7394,7 @@ public class Activity extends ContextThemeWrapper * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE */ public void setImmersive(boolean i) { - try { - ActivityTaskManager.getService().setImmersive(mToken, i); - } catch (RemoteException e) { - // pass - } + ActivityClient.getInstance().setImmersive(mToken, i); } /** @@ -7567,14 +7453,8 @@ public class Activity extends ContextThemeWrapper */ public void setVrModeEnabled(boolean enabled, @NonNull ComponentName requestedComponent) throws PackageManager.NameNotFoundException { - try { - if (ActivityTaskManager.getService().setVrMode(mToken, enabled, requestedComponent) - != 0) { - throw new PackageManager.NameNotFoundException( - requestedComponent.flattenToString()); - } - } catch (RemoteException e) { - // pass + if (ActivityClient.getInstance().setVrMode(mToken, enabled, requestedComponent) != 0) { + throw new PackageManager.NameNotFoundException(requestedComponent.flattenToString()); } } @@ -7689,9 +7569,7 @@ public class Activity extends ContextThemeWrapper if (info.taskAffinity == null) { return false; } - return ActivityTaskManager.getService().shouldUpRecreateTask(mToken, info.taskAffinity); - } catch (RemoteException e) { - return false; + return ActivityClient.getInstance().shouldUpRecreateTask(mToken, info.taskAffinity); } catch (NameNotFoundException e) { return false; } @@ -7739,13 +7617,9 @@ public class Activity extends ContextThemeWrapper if (resultData != null) { resultData.prepareToLeaveProcess(this); } - try { - upIntent.prepareToLeaveProcess(this); - return ActivityTaskManager.getService().navigateUpTo(mToken, upIntent, - resultCode, resultData); - } catch (RemoteException e) { - return false; - } + upIntent.prepareToLeaveProcess(this); + return ActivityClient.getInstance().navigateUpTo(mToken, upIntent, resultCode, + resultData); } else { return mParent.navigateUpToFromChild(this, upIntent); } @@ -8361,10 +8235,7 @@ public class Activity extends ContextThemeWrapper * @see android.R.attr#lockTaskMode */ public void startLockTask() { - try { - ActivityTaskManager.getService().startLockTaskModeByToken(mToken); - } catch (RemoteException e) { - } + ActivityClient.getInstance().startLockTaskModeByToken(mToken); } /** @@ -8384,10 +8255,7 @@ public class Activity extends ContextThemeWrapper * @see ActivityManager#getLockTaskModeState() */ public void stopLockTask() { - try { - ActivityTaskManager.getService().stopLockTaskModeByToken(mToken); - } catch (RemoteException e) { - } + ActivityClient.getInstance().stopLockTaskModeByToken(mToken); } /** @@ -8396,10 +8264,7 @@ public class Activity extends ContextThemeWrapper * of this call for the message to be displayed. */ public void showLockTaskEscapeMessage() { - try { - ActivityTaskManager.getService().showLockTaskEscapeMessage(mToken); - } catch (RemoteException e) { - } + ActivityClient.getInstance().showLockTaskEscapeMessage(mToken); } /** @@ -8667,11 +8532,7 @@ public class Activity extends ContextThemeWrapper */ @UnsupportedAppUsage public void setDisablePreviewScreenshots(boolean disable) { - try { - ActivityTaskManager.getService().setDisablePreviewScreenshots(mToken, disable); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + ActivityClient.getInstance().setDisablePreviewScreenshots(mToken, disable); } /** @@ -8688,11 +8549,7 @@ public class Activity extends ContextThemeWrapper * @see android.R.attr#showWhenLocked */ public void setShowWhenLocked(boolean showWhenLocked) { - try { - ActivityTaskManager.getService().setShowWhenLocked(mToken, showWhenLocked); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + ActivityClient.getInstance().setShowWhenLocked(mToken, showWhenLocked); } /** @@ -8711,12 +8568,7 @@ public class Activity extends ContextThemeWrapper * @see android.R.attr#inheritShowWhenLocked */ public void setInheritShowWhenLocked(boolean inheritShowWhenLocked) { - try { - ActivityTaskManager.getService().setInheritShowWhenLocked( - mToken, inheritShowWhenLocked); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + ActivityClient.getInstance().setInheritShowWhenLocked(mToken, inheritShowWhenLocked); } /** @@ -8741,11 +8593,7 @@ public class Activity extends ContextThemeWrapper * @see KeyguardManager#isDeviceSecure() */ public void setTurnScreenOn(boolean turnScreenOn) { - try { - ActivityTaskManager.getService().setTurnScreenOn(mToken, turnScreenOn); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + ActivityClient.getInstance().setTurnScreenOn(mToken, turnScreenOn); } /** @@ -8757,11 +8605,7 @@ public class Activity extends ContextThemeWrapper */ @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS) public void registerRemoteAnimations(RemoteAnimationDefinition definition) { - try { - ActivityTaskManager.getService().registerRemoteAnimations(mToken, definition); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + ActivityClient.getInstance().registerRemoteAnimations(mToken, definition); } /** @@ -8771,11 +8615,7 @@ public class Activity extends ContextThemeWrapper */ @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS) public void unregisterRemoteAnimations() { - try { - ActivityTaskManager.getService().unregisterRemoteAnimations(mToken); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + ActivityClient.getInstance().unregisterRemoteAnimations(mToken); } class HostCallbacks extends FragmentHostCallback<Activity> { diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java new file mode 100644 index 000000000000..84ecd24b8c55 --- /dev/null +++ b/core/java/android/app/ActivityClient.java @@ -0,0 +1,476 @@ +/* + * 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; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.IBinder; +import android.os.PersistableBundle; +import android.os.RemoteException; +import android.util.Singleton; +import android.view.RemoteAnimationDefinition; + +/** + * Provides the activity associated operations that communicate with system. + * + * @hide + */ +public class ActivityClient { + private ActivityClient() {} + + /** Reports the main thread is idle after the activity is resumed. */ + public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) { + try { + getActivityClientController().activityIdle(token, config, stopProfiling); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** Reports {@link Activity#onResume()} is done. */ + public void activityResumed(IBinder token) { + try { + getActivityClientController().activityResumed(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Reports after {@link Activity#onTopResumedActivityChanged(boolean)} is called for losing the + * top most position. + */ + public void activityTopResumedStateLost() { + try { + getActivityClientController().activityTopResumedStateLost(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** Reports {@link Activity#onPause()} is done. */ + public void activityPaused(IBinder token) { + try { + getActivityClientController().activityPaused(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** Reports {@link Activity#onStop()} is done. */ + public void activityStopped(IBinder token, Bundle state, PersistableBundle persistentState, + CharSequence description) { + try { + getActivityClientController().activityStopped(token, state, persistentState, + description); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** Reports {@link Activity#onDestroy()} is done. */ + public void activityDestroyed(IBinder token) { + try { + getActivityClientController().activityDestroyed(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** Reports the activity has completed relaunched. */ + public void activityRelaunched(IBinder token) { + try { + getActivityClientController().activityRelaunched(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration, + int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) { + try { + getActivityClientController().reportSizeConfigurations(token, + horizontalSizeConfiguration, verticalSizeConfigurations, + smallestSizeConfigurations); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) { + try { + return getActivityClientController().moveActivityTaskToBack(token, nonRoot); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + boolean shouldUpRecreateTask(IBinder token, String destAffinity) { + try { + return getActivityClientController().shouldUpRecreateTask(token, destAffinity); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode, + Intent resultData) { + try { + return getActivityClientController().navigateUpTo(token, destIntent, resultCode, + resultData); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + boolean releaseActivityInstance(IBinder token) { + try { + return getActivityClientController().releaseActivityInstance(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public boolean finishActivity(IBinder token, int resultCode, Intent resultData, + int finishTask) { + try { + return getActivityClientController().finishActivity(token, resultCode, resultData, + finishTask); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + boolean finishActivityAffinity(IBinder token) { + try { + return getActivityClientController().finishActivityAffinity(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void finishSubActivity(IBinder token, String resultWho, int requestCode) { + try { + getActivityClientController().finishSubActivity(token, resultWho, requestCode); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + public boolean isTopOfTask(IBinder token) { + try { + return getActivityClientController().isTopOfTask(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + boolean willActivityBeVisible(IBinder token) { + try { + return getActivityClientController().willActivityBeVisible(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public int getDisplayId(IBinder token) { + try { + return getActivityClientController().getDisplayId(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public int getTaskForActivity(IBinder token, boolean onlyRoot) { + try { + return getActivityClientController().getTaskForActivity(token, onlyRoot); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + ComponentName getCallingActivity(IBinder token) { + try { + return getActivityClientController().getCallingActivity(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + String getCallingPackage(IBinder token) { + try { + return getActivityClientController().getCallingPackage(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + Bundle getActivityOptions(IBinder token) { + try { + return getActivityClientController().getActivityOptions(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public void setRequestedOrientation(IBinder token, int requestedOrientation) { + try { + getActivityClientController().setRequestedOrientation(token, requestedOrientation); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + int getRequestedOrientation(IBinder token) { + try { + return getActivityClientController().getRequestedOrientation(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + boolean convertFromTranslucent(IBinder token) { + try { + return getActivityClientController().convertFromTranslucent(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + boolean convertToTranslucent(IBinder token, Bundle options) { + try { + return getActivityClientController().convertToTranslucent(token, options); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) { + try { + getActivityClientController().reportActivityFullyDrawn(token, restoredFromBundle); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + boolean isImmersive(IBinder token) { + try { + return getActivityClientController().isImmersive(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void setImmersive(IBinder token, boolean immersive) { + try { + getActivityClientController().setImmersive(token, immersive); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + boolean enterPictureInPictureMode(IBinder token, PictureInPictureParams params) { + try { + return getActivityClientController().enterPictureInPictureMode(token, params); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void setPictureInPictureParams(IBinder token, PictureInPictureParams params) { + try { + getActivityClientController().setPictureInPictureParams(token, params); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void toggleFreeformWindowingMode(IBinder token) { + try { + getActivityClientController().toggleFreeformWindowingMode(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void startLockTaskModeByToken(IBinder token) { + try { + getActivityClientController().startLockTaskModeByToken(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void stopLockTaskModeByToken(IBinder token) { + try { + getActivityClientController().stopLockTaskModeByToken(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void showLockTaskEscapeMessage(IBinder token) { + try { + getActivityClientController().showLockTaskEscapeMessage(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) { + try { + getActivityClientController().setTaskDescription(token, td); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + boolean showAssistFromActivity(IBinder token, Bundle args) { + try { + return getActivityClientController().showAssistFromActivity(token, args); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + boolean isRootVoiceInteraction(IBinder token) { + try { + return getActivityClientController().isRootVoiceInteraction(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) { + try { + getActivityClientController().startLocalVoiceInteraction(callingActivity, options); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void stopLocalVoiceInteraction(IBinder callingActivity) { + try { + getActivityClientController().stopLocalVoiceInteraction(callingActivity); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void setShowWhenLocked(IBinder token, boolean showWhenLocked) { + try { + getActivityClientController().setShowWhenLocked(token, showWhenLocked); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void setInheritShowWhenLocked(IBinder token, boolean inheritShowWhenLocked) { + try { + getActivityClientController().setInheritShowWhenLocked(token, inheritShowWhenLocked); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void setTurnScreenOn(IBinder token, boolean turnScreenOn) { + try { + getActivityClientController().setTurnScreenOn(token, turnScreenOn); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + int setVrMode(IBinder token, boolean enabled, ComponentName packageName) { + try { + return getActivityClientController().setVrMode(token, enabled, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void overridePendingTransition(IBinder token, String packageName, + int enterAnim, int exitAnim) { + try { + getActivityClientController().overridePendingTransition(token, packageName, + enterAnim, exitAnim); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void setDisablePreviewScreenshots(IBinder token, boolean disable) { + try { + getActivityClientController().setDisablePreviewScreenshots(token, disable); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) { + try { + getActivityClientController().registerRemoteAnimations(token, definition); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void unregisterRemoteAnimations(IBinder token) { + try { + getActivityClientController().unregisterRemoteAnimations(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void onBackPressedOnTaskRoot(IBinder token) { + try { + getActivityClientController().onBackPressedOnTaskRoot(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + public static ActivityClient getInstance() { + return sInstance.get(); + } + + private static IActivityClientController getActivityClientController() { + return sActivityClientController.get(); + } + + private static final Singleton<ActivityClient> sInstance = new Singleton<ActivityClient>() { + @Override + protected ActivityClient create() { + return new ActivityClient(); + } + }; + + private static final Singleton<IActivityClientController> sActivityClientController = + new Singleton<IActivityClientController>() { + @Override + protected IActivityClientController create() { + try { + return ActivityTaskManager.getService().getActivityClientController(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + }; +} diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index e3048dff1bda..38a22d8ade94 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -77,6 +77,8 @@ import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Singleton; import android.util.Size; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.view.Surface; import android.view.WindowInsetsController.Appearance; @@ -1502,57 +1504,54 @@ public class ActivityManager { } /** @hide */ - public void saveToXml(XmlSerializer out) throws IOException { + public void saveToXml(TypedXmlSerializer out) throws IOException { if (mLabel != null) { out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, mLabel); } if (mColorPrimary != 0) { - out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_PRIMARY, - Integer.toHexString(mColorPrimary)); + out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_PRIMARY, mColorPrimary); } if (mColorBackground != 0) { - out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, - Integer.toHexString(mColorBackground)); + out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, mColorBackground); } if (mIconFilename != null) { out.attribute(null, ATTR_TASKDESCRIPTIONICON_FILENAME, mIconFilename); } if (mIcon != null && mIcon.getType() == Icon.TYPE_RESOURCE) { - out.attribute(null, ATTR_TASKDESCRIPTIONICON_RESOURCE, - Integer.toString(mIcon.getResId())); + out.attributeInt(null, ATTR_TASKDESCRIPTIONICON_RESOURCE, mIcon.getResId()); out.attribute(null, ATTR_TASKDESCRIPTIONICON_RESOURCE_PACKAGE, mIcon.getResPackage()); } } /** @hide */ - public void restoreFromXml(XmlPullParser in) { + public void restoreFromXml(TypedXmlPullParser in) { final String label = in.getAttributeValue(null, ATTR_TASKDESCRIPTIONLABEL); if (label != null) { setLabel(label); } - final String colorPrimary = in.getAttributeValue(null, - ATTR_TASKDESCRIPTIONCOLOR_PRIMARY); - if (colorPrimary != null) { - setPrimaryColor((int) Long.parseLong(colorPrimary, 16)); + final int colorPrimary = in.getAttributeIntHex(null, + ATTR_TASKDESCRIPTIONCOLOR_PRIMARY, 0); + if (colorPrimary != 0) { + setPrimaryColor(colorPrimary); } - final String colorBackground = in.getAttributeValue(null, - ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND); - if (colorBackground != null) { - setBackgroundColor((int) Long.parseLong(colorBackground, 16)); + final int colorBackground = in.getAttributeIntHex(null, + ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, 0); + if (colorBackground != 0) { + setBackgroundColor(colorBackground); } final String iconFilename = in.getAttributeValue(null, ATTR_TASKDESCRIPTIONICON_FILENAME); if (iconFilename != null) { setIconFilename(iconFilename); } - final String iconResourceId = in.getAttributeValue(null, - ATTR_TASKDESCRIPTIONICON_RESOURCE); + final int iconResourceId = in.getAttributeInt(null, + ATTR_TASKDESCRIPTIONICON_RESOURCE, Resources.ID_NULL); final String iconResourcePackage = in.getAttributeValue(null, ATTR_TASKDESCRIPTIONICON_RESOURCE_PACKAGE); - if (iconResourceId != null && iconResourcePackage != null) { + if (iconResourceId != Resources.ID_NULL && iconResourcePackage != null) { setIcon(Icon.createWithResource(iconResourcePackage, - Integer.parseInt(iconResourceId, 10))); + iconResourceId)); } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d9c0c71d44ae..ed6dea815e4c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -85,6 +85,7 @@ import android.database.sqlite.SQLiteDebug.DbStats; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.HardwareRenderer; +import android.graphics.Rect; import android.graphics.Typeface; import android.hardware.display.DisplayManagerGlobal; import android.inputmethodservice.InputMethodService; @@ -2154,7 +2155,7 @@ public final class ActivityThread extends ClientTransactionHandler { } if (a != null) { mNewActivities = null; - IActivityTaskManager am = ActivityTaskManager.getService(); + final ActivityClient ac = ActivityClient.getInstance(); ActivityClientRecord prev; do { if (localLOGV) Slog.v( @@ -2162,12 +2163,8 @@ public final class ActivityThread extends ClientTransactionHandler { " finished=" + (a.activity != null && a.activity.mFinished)); if (a.activity != null && !a.activity.mFinished) { - try { - am.activityIdle(a.token, a.createdConfig, stopProfiling); - a.createdConfig = null; - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + ac.activityIdle(a.token, a.createdConfig, stopProfiling); + a.createdConfig = null; } prev = a; a = a.nextIdle; @@ -3576,13 +3573,7 @@ public final class ActivityThread extends ClientTransactionHandler { } private ContextImpl createBaseContextForActivity(ActivityClientRecord r) { - final int displayId; - try { - displayId = ActivityTaskManager.getService().getDisplayId(r.token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - + final int displayId = ActivityClient.getInstance().getDisplayId(r.token); ContextImpl appContext = ContextImpl.createActivityContext( this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); @@ -3669,13 +3660,8 @@ public final class ActivityThread extends ClientTransactionHandler { } } else { // If there was an error, for any reason, tell the activity manager to stop us. - try { - ActivityTaskManager.getService() - .finishActivity(r.token, Activity.RESULT_CANCELED, null, - Activity.DONT_FINISH_TASK_WITH_ACTIVITY); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED, + null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY); } return a; @@ -3705,12 +3691,8 @@ public final class ActivityThread extends ClientTransactionHandler { smallest.put(config.smallestScreenWidthDp, 0); } } - try { - ActivityTaskManager.getService().reportSizeConfigurations(r.token, - horizontal.copyKeys(), vertical.copyKeys(), smallest.copyKeys()); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + ActivityClient.getInstance().reportSizeConfigurations(r.token, horizontal.copyKeys(), + vertical.copyKeys(), smallest.copyKeys()); } private void deliverNewIntents(ActivityClientRecord r, List<ReferrerIntent> intents) { @@ -4559,12 +4541,8 @@ public final class ActivityThread extends ClientTransactionHandler { // then go ahead and add the window. boolean willBeVisible = !a.mStartedActivity; if (!willBeVisible) { - try { - willBeVisible = ActivityTaskManager.getService().willActivityBeVisible( - a.getActivityToken()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + willBeVisible = ActivityClient.getInstance().willActivityBeVisible( + a.getActivityToken()); } if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); @@ -5209,11 +5187,7 @@ public final class ActivityThread extends ClientTransactionHandler { ((ContextImpl) c).scheduleFinalCleanup(r.activity.getClass().getName(), "Activity"); } if (finishing) { - try { - ActivityTaskManager.getService().activityDestroyed(r.token); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + ActivityClient.getInstance().activityDestroyed(r.token); } mSomeActivitiesChanged = true; } @@ -5477,13 +5451,9 @@ public final class ActivityThread extends ClientTransactionHandler { @Override public void reportRelaunch(ActivityClientRecord r, PendingTransactionActions pendingActions) { - try { - ActivityTaskManager.getService().activityRelaunched(r.token); - if (pendingActions.shouldReportRelaunchToWindowManager() && r.window != null) { - r.window.reportActivityRelaunched(); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + ActivityClient.getInstance().activityRelaunched(r.token); + if (pendingActions.shouldReportRelaunchToWindowManager() && r.window != null) { + r.window.reportActivityRelaunched(); } } @@ -5623,10 +5593,13 @@ public final class ActivityThread extends ClientTransactionHandler { // If the new config is the same as the config this Activity is already running with and // the override config also didn't change, then don't bother calling // onConfigurationChanged. + // TODO(b/173090263): Use diff instead after the improvement of AssetManager and + // ResourcesImpl constructions. final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig); - if (diff == 0 && !movedToDifferentDisplay - && mResourcesManager.isSameResourcesOverrideConfig(activityToken, - amOverrideConfig)) { + + if (diff == 0 && !shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig) + && !movedToDifferentDisplay && mResourcesManager.isSameResourcesOverrideConfig( + activityToken, amOverrideConfig)) { // Nothing significant, don't proceed with updating and reporting. return null; } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) { @@ -5678,6 +5651,26 @@ public final class ActivityThread extends ClientTransactionHandler { return configToReport; } + // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl + // constructions. + /** + * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs + * should be updated. + * + * @see WindowManager#getCurrentWindowMetrics() + * @see WindowManager#getMaximumWindowMetrics() + */ + private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig, + @NonNull Configuration newConfig) { + final Rect currentBounds = currentConfig.windowConfiguration.getBounds(); + final Rect newBounds = newConfig.windowConfiguration.getBounds(); + + final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds(); + final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds(); + + return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds); + } + public final void applyConfigurationToResources(Configuration config) { synchronized (mResourcesManager) { mResourcesManager.applyConfigurationToResourcesLocked(config, null); @@ -5912,7 +5905,7 @@ public final class ActivityThread extends ClientTransactionHandler { /** * Sets the supplied {@code overrideConfig} as pending for the {@code activityToken}. Calling * this method prevents any calls to - * {@link #handleActivityConfigurationChanged(IBinder, Configuration, int, boolean)} from + * {@link #handleActivityConfigurationChanged(ActivityClientRecord, Configuration, int)} from * processing any configurations older than {@code overrideConfig}. */ @Override @@ -5934,8 +5927,8 @@ public final class ActivityThread extends ClientTransactionHandler { /** * Handle new activity configuration and/or move to a different display. This method is a noop - * if {@link #updatePendingActivityConfiguration(IBinder, Configuration)} has been called with - * a newer config than {@code overrideConfig}. + * if {@link #updatePendingActivityConfiguration(ActivityClientRecord, Configuration)} has been + * called with a newer config than {@code overrideConfig}. * * @param r Target activity record. * @param overrideConfig Activity override config. @@ -6000,7 +5993,7 @@ public final class ActivityThread extends ClientTransactionHandler { /** * Checks if the display id of activity is different from the given one. Note that - * {@link #INVALID_DISPLAY} means no difference. + * {@link Display#INVALID_DISPLAY} means no difference. */ private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) { return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId(); @@ -6417,11 +6410,13 @@ public final class ActivityThread extends ClientTransactionHandler { */ LocaleList.setDefault(data.config.getLocales()); - try { - Typeface.setSystemFontMap(data.mSerializedSystemFontMap); - } catch (IOException | ErrnoException e) { - Slog.e(TAG, "Failed to parse serialized system font map"); - Typeface.loadPreinstalledSystemFontMap(); + if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + try { + Typeface.setSystemFontMap(data.mSerializedSystemFontMap); + } catch (IOException | ErrnoException e) { + Slog.e(TAG, "Failed to parse serialized system font map"); + Typeface.loadPreinstalledSystemFontMap(); + } } synchronized (mResourcesManager) { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 34437afb614a..3642d318e820 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -30,6 +30,7 @@ import android.annotation.Nullable; import android.annotation.StringRes; import android.annotation.UserIdInt; import android.annotation.XmlRes; +import android.app.role.RoleManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; @@ -2306,20 +2307,14 @@ public class ApplicationPackageManager extends PackageManager { @Override public String getDefaultBrowserPackageNameAsUser(int userId) { - try { - return mPermissionManager.getDefaultBrowser(userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + return roleManager.getBrowserRoleHolder(userId); } @Override public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) { - try { - return mPermissionManager.setDefaultBrowser(packageName, userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + return roleManager.setBrowserRoleHolder(packageName, userId); } @Override diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl new file mode 100644 index 000000000000..f9449f241545 --- /dev/null +++ b/core/java/android/app/IActivityClientController.aidl @@ -0,0 +1,108 @@ +/* + * 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; + +import android.app.ActivityManager; +import android.app.PictureInPictureParams; +import android.content.ComponentName; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.PersistableBundle; +import android.view.RemoteAnimationDefinition; + +/** + * Interface for the callback and request from an activity to system. + * + * {@hide} + */ +interface IActivityClientController { + oneway void activityIdle(in IBinder token, in Configuration config, in boolean stopProfiling); + void activityResumed(in IBinder token); + void activityTopResumedStateLost(); + void activityPaused(in IBinder token); + void activityStopped(in IBinder token, in Bundle state, in PersistableBundle persistentState, + in CharSequence description); + oneway void activityDestroyed(in IBinder token); + void activityRelaunched(in IBinder token); + + void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration, + in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations); + boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot); + boolean shouldUpRecreateTask(in IBinder token, in String destAffinity); + boolean navigateUpTo(in IBinder token, in Intent target, int resultCode, + in Intent resultData); + boolean releaseActivityInstance(in IBinder token); + boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask); + boolean finishActivityAffinity(in IBinder token); + /** Finish all activities that were started for result from the specified activity. */ + void finishSubActivity(in IBinder token, in String resultWho, int requestCode); + + boolean isTopOfTask(in IBinder token); + boolean willActivityBeVisible(in IBinder token); + int getDisplayId(in IBinder activityToken); + int getTaskForActivity(in IBinder token, in boolean onlyRoot); + ComponentName getCallingActivity(in IBinder token); + String getCallingPackage(in IBinder token); + Bundle getActivityOptions(in IBinder token); + + void setRequestedOrientation(in IBinder token, int requestedOrientation); + int getRequestedOrientation(in IBinder token); + + boolean convertFromTranslucent(in IBinder token); + boolean convertToTranslucent(in IBinder token, in Bundle options); + + boolean isImmersive(in IBinder token); + void setImmersive(in IBinder token, boolean immersive); + + boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params); + void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params); + void toggleFreeformWindowingMode(in IBinder token); + + void startLockTaskModeByToken(in IBinder token); + void stopLockTaskModeByToken(in IBinder token); + oneway void showLockTaskEscapeMessage(in IBinder token); + void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values); + + boolean showAssistFromActivity(in IBinder token, in Bundle args); + boolean isRootVoiceInteraction(in IBinder token); + void startLocalVoiceInteraction(in IBinder token, in Bundle options); + void stopLocalVoiceInteraction(in IBinder token); + + void setShowWhenLocked(in IBinder token, boolean showWhenLocked); + void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked); + void setTurnScreenOn(in IBinder token, boolean turnScreenOn); + void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle); + void overridePendingTransition(in IBinder token, in String packageName, + int enterAnim, int exitAnim); + int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName); + + /** See {@link android.app.Activity#setDisablePreviewScreenshots}. */ + void setDisablePreviewScreenshots(in IBinder token, boolean disable); + + /** Registers remote animations for a specific activity. */ + void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition); + + /** Unregisters all remote animations for a specific activity. */ + void unregisterRemoteAnimations(in IBinder token); + + /** + * Reports that an Activity received a back key press when there were no additional activities + * on the back stack. + */ + void onBackPressedOnTaskRoot(in IBinder token); +} diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index ab48baea48e8..4b2557389382 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -22,6 +22,7 @@ import android.app.ApplicationErrorReport; import android.app.ContentProviderHolder; import android.app.GrantedUriPermission; import android.app.IApplicationThread; +import android.app.IActivityClientController; import android.app.IActivityController; import android.app.IAppTask; import android.app.IAssistDataReceiver; @@ -35,7 +36,6 @@ import android.app.IUidObserver; import android.app.IUserSwitchObserver; import android.app.Notification; import android.app.PendingIntent; -import android.app.PictureInPictureParams; import android.app.ProfilerInfo; import android.app.WaitResult; import android.app.assist.AssistContent; @@ -63,7 +63,6 @@ import android.os.Debug; import android.os.IBinder; import android.os.IProgressListener; import android.os.ParcelFileDescriptor; -import android.os.PersistableBundle; import android.os.StrictMode; import android.os.WorkSource; import android.service.voice.IVoiceInteractionSession; @@ -144,54 +143,25 @@ interface IActivityTaskManager { int userId); void unhandledBack(); - boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask); - boolean finishActivityAffinity(in IBinder token); - - oneway void activityIdle(in IBinder token, in Configuration config, - in boolean stopProfiling); - void activityResumed(in IBinder token); - void activityTopResumedStateLost(); - void activityPaused(in IBinder token); - void activityStopped(in IBinder token, in Bundle state, - in PersistableBundle persistentState, in CharSequence description); - oneway void activityDestroyed(in IBinder token); - void activityRelaunched(in IBinder token); + + /** Returns an interface to control the activity related operations. */ + IActivityClientController getActivityClientController(); + int getFrontActivityScreenCompatMode(); void setFrontActivityScreenCompatMode(int mode); - String getCallingPackage(in IBinder token); - ComponentName getCallingActivity(in IBinder token); void setFocusedTask(int taskId); boolean removeTask(int taskId); void removeAllVisibleRecentTasks(); List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents); - boolean shouldUpRecreateTask(in IBinder token, in String destAffinity); - boolean navigateUpTo(in IBinder token, in Intent target, int resultCode, - in Intent resultData); void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task, int flags, in Bundle options); - int getTaskForActivity(in IBinder token, in boolean onlyRoot); - /** Finish all activities that were started for result from the specified activity. */ - void finishSubActivity(in IBinder token, in String resultWho, int requestCode); ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId); - boolean willActivityBeVisible(in IBinder token); - void setRequestedOrientation(in IBinder token, int requestedOrientation); - int getRequestedOrientation(in IBinder token); - boolean convertFromTranslucent(in IBinder token); - boolean convertToTranslucent(in IBinder token, in Bundle options); - void notifyActivityDrawn(in IBinder token); - void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle); - int getDisplayId(in IBinder activityToken); - boolean isImmersive(in IBinder token); - void setImmersive(in IBinder token, boolean immersive); boolean isTopActivityImmersive(); - boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot); ActivityManager.TaskDescription getTaskDescription(int taskId); - void overridePendingTransition(in IBinder token, in String packageName, - int enterAnim, int exitAnim); int getLaunchedFromUid(in IBinder activityToken); String getLaunchedFromPackage(in IBinder activityToken); - void reportAssistContextExtras(in IBinder token, in Bundle extras, + void reportAssistContextExtras(in IBinder assistToken, in Bundle extras, in AssistStructure structure, in AssistContent content, in Uri referrer); void setFocusedRootTask(int taskId); @@ -199,24 +169,16 @@ interface IActivityTaskManager { Rect getTaskBounds(int taskId); void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition); - void startLockTaskModeByToken(in IBinder token); - void stopLockTaskModeByToken(in IBinder token); void updateLockTaskPackages(int userId, in String[] packages); boolean isInLockTaskMode(); int getLockTaskModeState(); - void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values); - Bundle getActivityOptions(in IBinder token); List<IBinder> getAppTasks(in String callingPackage); void startSystemLockTaskMode(int taskId); void stopSystemLockTaskMode(); void finishVoiceTask(in IVoiceInteractionSession session); - boolean isTopOfTask(in IBinder token); - void notifyLaunchTaskBehindComplete(in IBinder token); - void notifyEnterAnimationComplete(in IBinder token); int addAppTask(in IBinder activityToken, in Intent intent, in ActivityManager.TaskDescription description, in Bitmap thumbnail); Point getAppTaskThumbnailSize(); - boolean releaseActivityInstance(in IBinder token); /** * Only callable from the system. This token grants a temporary permission to call * #startActivityAsCallerWithToken. The token will time out after @@ -236,7 +198,6 @@ interface IActivityTaskManager { void registerTaskStackListener(in ITaskStackListener listener); void unregisterTaskStackListener(in ITaskStackListener listener); void setTaskResizeable(int taskId, int resizeableMode); - void toggleFreeformWindowingMode(in IBinder token); /** * Resize the task with given bounds @@ -287,9 +248,6 @@ interface IActivityTaskManager { boolean requestAutofillData(in IAssistDataReceiver receiver, in Bundle receiverExtras, in IBinder activityToken, int flags); boolean isAssistDataAllowedOnCurrentActivity(); - boolean showAssistFromActivity(in IBinder token, in Bundle args); - boolean isRootVoiceInteraction(in IBinder token); - oneway void showLockTaskEscapeMessage(in IBinder token); /** * Notify the system that the keyguard is going away. @@ -302,15 +260,9 @@ interface IActivityTaskManager { ComponentName getActivityClassForToken(in IBinder token); String getPackageForToken(in IBinder token); - void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration, - in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations); - void suppressResizeConfigChanges(boolean suppress); boolean moveTopActivityToPinnedRootTask(int rootTaskId, in Rect bounds); - boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params); - void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params); void requestPictureInPictureMode(in IBinder token); - IBinder getUriPermissionOwnerForActivity(in IBinder activityToken); /** * Resizes the docked stack, and all other stacks as the result of the dock stack bounds change. @@ -343,9 +295,6 @@ interface IActivityTaskManager { * are changing the docked stack size. */ void setSplitScreenResizing(boolean resizing); - int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName); - void startLocalVoiceInteraction(in IBinder token, in Bundle options); - void stopLocalVoiceInteraction(in IBinder token); boolean supportsLocalVoiceInteraction(); // Get device configuration @@ -366,11 +315,6 @@ interface IActivityTaskManager { ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution); /** - * See {@link android.app.Activity#setDisablePreviewScreenshots} - */ - void setDisablePreviewScreenshots(IBinder token, boolean disable); - - /** * It should only be called from home activity to remove its outdated snapshot. The home * snapshot is used to speed up entering home from screen off. If the content of home activity * is significantly different from before taking the snapshot, then the home activity can use @@ -393,20 +337,6 @@ interface IActivityTaskManager { boolean updateConfiguration(in Configuration values); void updateLockTaskFeatures(int userId, int flags); - void setShowWhenLocked(in IBinder token, boolean showWhenLocked); - void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked); - void setTurnScreenOn(in IBinder token, boolean turnScreenOn); - - /** - * Registers remote animations for a specific activity. - */ - void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition); - - /** - * Unregisters all remote animations for a specific activity. - */ - void unregisterRemoteAnimations(in IBinder token); - /** * Registers a remote animation to be run for all activity starts from a certain package during * a short predefined amount of time. @@ -448,10 +378,4 @@ interface IActivityTaskManager { * @param activityToken The token of the target activity to restart. */ void restartActivityProcessIfVisible(in IBinder activityToken); - - /** - * Reports that an Activity received a back key press when there were no additional activities - * on the back stack. - */ - void onBackPressedOnTaskRoot(in IBinder activityToken); } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 6535387acdf3..99785e1e73f4 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1284,7 +1284,7 @@ public final class LoadedApk { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); throw new RuntimeException( "Unable to instantiate application " + appClass - + ": " + e.toString(), e); + + " package " + mPackageName + ": " + e.toString(), e); } } mActivityThread.mAllApplications.add(app); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a1135809fd4c..82f61a4c615a 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -83,6 +83,7 @@ import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -6120,8 +6121,11 @@ public class Notification implements Parcelable int defaultColor = mInNightMode ? Color.BLACK : Color.WHITE; Resources.Theme theme = mContext.getTheme(); if (theme == null) { + // Running unit tests with mocked context return defaultColor; } + theme = new ContextThemeWrapper(mContext, R.style.Theme_DeviceDefault_DayNight) + .getTheme(); TypedArray ta = theme.obtainStyledAttributes(new int[]{R.attr.colorBackground}); if (ta == null) { return defaultColor; diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 080aac9a9e6a..b1a8f9b0ba33 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -32,9 +32,12 @@ import android.os.Parcelable; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.text.TextUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.util.Preconditions; +import com.android.internal.util.XmlUtils; import org.json.JSONException; import org.json.JSONObject; @@ -869,7 +872,7 @@ public final class NotificationChannel implements Parcelable { * @hide */ public void populateFromXmlForRestore(XmlPullParser parser, Context context) { - populateFromXml(parser, true, context); + populateFromXml(XmlUtils.makeTyped(parser), true, context); } /** @@ -877,13 +880,13 @@ public final class NotificationChannel implements Parcelable { */ @SystemApi public void populateFromXml(XmlPullParser parser) { - populateFromXml(parser, false, null); + populateFromXml(XmlUtils.makeTyped(parser), false, null); } /** * If {@param forRestore} is true, {@param Context} MUST be non-null. */ - private void populateFromXml(XmlPullParser parser, boolean forRestore, + private void populateFromXml(TypedXmlPullParser parser, boolean forRestore, @Nullable Context context) { Preconditions.checkArgument(!forRestore || context != null, "forRestore is true but got null context"); @@ -941,14 +944,14 @@ public final class NotificationChannel implements Parcelable { */ @SystemApi public void writeXml(XmlSerializer out) throws IOException { - writeXml(out, false, null); + writeXml(XmlUtils.makeTyped(out), false, null); } /** * @hide */ public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException { - writeXml(out, true, context); + writeXml(XmlUtils.makeTyped(out), true, context); } private Uri getSoundForBackup(Context context) { @@ -967,7 +970,7 @@ public final class NotificationChannel implements Parcelable { /** * If {@param forBackup} is true, {@param Context} MUST be non-null. */ - private void writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context) + private void writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context) throws IOException { Preconditions.checkArgument(!forBackup || context != null, "forBackup is true but got null context"); @@ -980,62 +983,58 @@ public final class NotificationChannel implements Parcelable { out.attribute(null, ATT_DESC, getDescription()); } if (getImportance() != DEFAULT_IMPORTANCE) { - out.attribute( - null, ATT_IMPORTANCE, Integer.toString(getImportance())); + out.attributeInt(null, ATT_IMPORTANCE, getImportance()); } if (canBypassDnd()) { - out.attribute( - null, ATT_PRIORITY, Integer.toString(Notification.PRIORITY_MAX)); + out.attributeInt(null, ATT_PRIORITY, Notification.PRIORITY_MAX); } if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { - out.attribute(null, ATT_VISIBILITY, - Integer.toString(getLockscreenVisibility())); + out.attributeInt(null, ATT_VISIBILITY, getLockscreenVisibility()); } Uri sound = forBackup ? getSoundForBackup(context) : getSound(); if (sound != null) { out.attribute(null, ATT_SOUND, sound.toString()); } if (getAudioAttributes() != null) { - out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage())); - out.attribute(null, ATT_CONTENT_TYPE, - Integer.toString(getAudioAttributes().getContentType())); - out.attribute(null, ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags())); + out.attributeInt(null, ATT_USAGE, getAudioAttributes().getUsage()); + out.attributeInt(null, ATT_CONTENT_TYPE, getAudioAttributes().getContentType()); + out.attributeInt(null, ATT_FLAGS, getAudioAttributes().getFlags()); } if (shouldShowLights()) { - out.attribute(null, ATT_LIGHTS, Boolean.toString(shouldShowLights())); + out.attributeBoolean(null, ATT_LIGHTS, shouldShowLights()); } if (getLightColor() != DEFAULT_LIGHT_COLOR) { - out.attribute(null, ATT_LIGHT_COLOR, Integer.toString(getLightColor())); + out.attributeInt(null, ATT_LIGHT_COLOR, getLightColor()); } if (shouldVibrate()) { - out.attribute(null, ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); + out.attributeBoolean(null, ATT_VIBRATION_ENABLED, shouldVibrate()); } if (getVibrationPattern() != null) { out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern())); } if (getUserLockedFields() != 0) { - out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); + out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields()); } if (isFgServiceShown()) { - out.attribute(null, ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown())); + out.attributeBoolean(null, ATT_FG_SERVICE_SHOWN, isFgServiceShown()); } if (canShowBadge()) { - out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); + out.attributeBoolean(null, ATT_SHOW_BADGE, canShowBadge()); } if (isDeleted()) { - out.attribute(null, ATT_DELETED, Boolean.toString(isDeleted())); + out.attributeBoolean(null, ATT_DELETED, isDeleted()); } if (getGroup() != null) { out.attribute(null, ATT_GROUP, getGroup()); } if (isBlockable()) { - out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockable())); + out.attributeBoolean(null, ATT_BLOCKABLE_SYSTEM, isBlockable()); } if (getAllowBubbles() != DEFAULT_ALLOW_BUBBLE) { - out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(getAllowBubbles())); + out.attributeInt(null, ATT_ALLOW_BUBBLE, getAllowBubbles()); } if (getOriginalImportance() != DEFAULT_IMPORTANCE) { - out.attribute(null, ATT_ORIG_IMP, Integer.toString(getOriginalImportance())); + out.attributeInt(null, ATT_ORIG_IMP, getOriginalImportance()); } if (getParentChannelId() != null) { out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId()); @@ -1044,10 +1043,10 @@ public final class NotificationChannel implements Parcelable { out.attribute(null, ATT_CONVERSATION_ID, getConversationId()); } if (isDemoted()) { - out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted())); + out.attributeBoolean(null, ATT_DEMOTE, isDemoted()); } if (isImportantConversation()) { - out.attribute(null, ATT_IMP_CONVERSATION, Boolean.toString(isImportantConversation())); + out.attributeBoolean(null, ATT_IMP_CONVERSATION, isImportantConversation()); } // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of @@ -1099,7 +1098,7 @@ public final class NotificationChannel implements Parcelable { return record; } - private static AudioAttributes safeAudioAttributes(XmlPullParser parser) { + private static AudioAttributes safeAudioAttributes(TypedXmlPullParser parser) { int usage = safeInt(parser, ATT_USAGE, AudioAttributes.USAGE_NOTIFICATION); int contentType = safeInt(parser, ATT_CONTENT_TYPE, AudioAttributes.CONTENT_TYPE_SONIFICATION); @@ -1111,32 +1110,20 @@ public final class NotificationChannel implements Parcelable { .build(); } - private static Uri safeUri(XmlPullParser parser, String att) { + private static Uri safeUri(TypedXmlPullParser parser, String att) { final String val = parser.getAttributeValue(null, att); return val == null ? null : Uri.parse(val); } - private static int safeInt(XmlPullParser parser, String att, int defValue) { - final String val = parser.getAttributeValue(null, att); - return tryParseInt(val, defValue); - } - - private static int tryParseInt(String value, int defValue) { - if (TextUtils.isEmpty(value)) return defValue; - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - return defValue; - } + private static int safeInt(TypedXmlPullParser parser, String att, int defValue) { + return parser.getAttributeInt(null, att, defValue); } - private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) { - final String value = parser.getAttributeValue(null, att); - if (TextUtils.isEmpty(value)) return defValue; - return Boolean.parseBoolean(value); + private static boolean safeBool(TypedXmlPullParser parser, String att, boolean defValue) { + return parser.getAttributeBoolean(null, att, defValue); } - private static long[] safeLongArray(XmlPullParser parser, String att, long[] defValue) { + private static long[] safeLongArray(TypedXmlPullParser parser, String att, long[] defValue) { final String attributeValue = parser.getAttributeValue(null, att); if (TextUtils.isEmpty(attributeValue)) return defValue; String[] values = attributeValue.split(DELIMITER); diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java index ec7fa332b23f..cd6df0b231d9 100644 --- a/core/java/android/app/NotificationChannelGroup.java +++ b/core/java/android/app/NotificationChannelGroup.java @@ -23,12 +23,12 @@ import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import org.json.JSONException; import org.json.JSONObject; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -228,22 +228,16 @@ public final class NotificationChannelGroup implements Parcelable { /** * @hide */ - public void populateFromXml(XmlPullParser parser) { + public void populateFromXml(TypedXmlPullParser parser) { // Name, id, and importance are set in the constructor. setDescription(parser.getAttributeValue(null, ATT_DESC)); - setBlocked(safeBool(parser, ATT_BLOCKED, false)); - } - - private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) { - final String value = parser.getAttributeValue(null, att); - if (TextUtils.isEmpty(value)) return defValue; - return Boolean.parseBoolean(value); + setBlocked(parser.getAttributeBoolean(null, ATT_BLOCKED, false)); } /** * @hide */ - public void writeXml(XmlSerializer out) throws IOException { + public void writeXml(TypedXmlSerializer out) throws IOException { out.startTag(null, TAG_GROUP); out.attribute(null, ATT_ID, getId()); @@ -253,8 +247,8 @@ public final class NotificationChannelGroup implements Parcelable { if (getDescription() != null) { out.attribute(null, ATT_DESC, getDescription().toString()); } - out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked())); - out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields)); + out.attributeBoolean(null, ATT_BLOCKED, isBlocked()); + out.attributeInt(null, ATT_USER_LOCKED, mUserLockedFields); out.endTag(null, TAG_GROUP); } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 36d5b5eb9fdf..772833cc6d2d 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -1178,6 +1178,8 @@ public class ResourcesManager { continue; } + // TODO(b/173090263): Improve the performance of AssetManager & ResourcesImpl + // constructions. final ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(newKey); if (resourcesImpl != null && resourcesImpl != resources.getImpl()) { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 7287acdd0241..392d6fbe53d6 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -194,6 +194,7 @@ import android.telephony.TelephonyRegistryManager; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; +import android.uwb.UwbManager; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.WindowManager; @@ -732,6 +733,14 @@ public final class SystemServiceRegistry { return new SerialManager(ctx, ISerialManager.Stub.asInterface(b)); }}); + registerService(Context.UWB_SERVICE, UwbManager.class, + new CachedServiceFetcher<UwbManager>() { + @Override + public UwbManager createService(ContextImpl ctx) { + return UwbManager.getInstance(); + } + }); + registerService(Context.VIBRATOR_SERVICE, Vibrator.class, new CachedServiceFetcher<Vibrator>() { @Override diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index 36241a8ebe08..61e93f73b094 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -70,6 +70,18 @@ "name": "CtsAutoFillServiceTestCases", "options": [ { + "include-filter": "android.autofillservice.cts.saveui.AutofillSaveDialogTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ], + "file_patterns": ["(/|^)Activity.java"] + }, + { + "name": "CtsAutoFillServiceTestCases", + "options": [ + { "include-filter": "android.autofillservice.cts.saveui.PreSimpleSaveActivityTest" }, { diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 1ee8e4fce58b..41256d09d0ed 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -37,11 +37,12 @@ import android.util.AttributeSet; import android.util.Log; import android.util.Printer; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -472,16 +473,15 @@ public final class DeviceAdminInfo implements Parcelable { } /** @hide */ - public void writePoliciesToXml(XmlSerializer out) + public void writePoliciesToXml(TypedXmlSerializer out) throws IllegalArgumentException, IllegalStateException, IOException { - out.attribute(null, "flags", Integer.toString(mUsesPolicies)); + out.attributeInt(null, "flags", mUsesPolicies); } /** @hide */ - public void readPoliciesFromXml(XmlPullParser parser) + public void readPoliciesFromXml(TypedXmlPullParser parser) throws XmlPullParserException, IOException { - mUsesPolicies = Integer.parseInt( - parser.getAttributeValue(null, "flags")); + mUsesPolicies = parser.getAttributeInt(null, "flags"); } public void dump(Printer pw, String prefix) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 2cda017bbd1a..4095acc239e9 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1207,7 +1207,7 @@ public class DevicePolicyManager { * <ul> * <li>By the admin app when performing the admin-integrated * provisioning flow as a result of the {@link #ACTION_GET_PROVISIONING_MODE} activity</li> - * <li>With intent action {@link #ACTION_PROVISION_MANAGED_DEVICE}</li> + * <li>For managed account enrollment</li> * </ul> * * <p>If the education screens are skipped, it is the admin application's responsibility @@ -1230,9 +1230,37 @@ public class DevicePolicyManager { "android.app.extra.PROVISIONING_USE_MOBILE_DATA"; /** + * Possible values for {@link #EXTRA_PROVISIONING_TRIGGER}. + * + * @hide + */ + @IntDef(prefix = { "PROVISIONING_TRIGGER_" }, value = { + PROVISIONING_TRIGGER_UNSPECIFIED, + PROVISIONING_TRIGGER_CLOUD_ENROLLMENT, + PROVISIONING_TRIGGER_QR_CODE, + PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER, + PROVISIONING_TRIGGER_MANAGED_ACCOUNT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ProvisioningTrigger {} + + /** + * Possible values for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES}. + * + * @hide + */ + @IntDef(prefix = { "SUPPORTED_MODES_" }, value = { + SUPPORTED_MODES_ORGANIZATION_OWNED, + SUPPORTED_MODES_PERSONALLY_OWNED, + SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ProvisioningConfiguration {} + + /** * A String extra holding the provisioning trigger. It could be one of * {@link #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT}, {@link #PROVISIONING_TRIGGER_QR_CODE}, - * {@link #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER} or {@link + * {@link #PROVISIONING_TRIGGER_MANAGED_ACCOUNT} or {@link * #PROVISIONING_TRIGGER_UNSPECIFIED}. * * <p>Use in an intent with action {@link @@ -1248,7 +1276,7 @@ public class DevicePolicyManager { * trigger has not been specified. * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT * @see #PROVISIONING_TRIGGER_QR_CODE - * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER + * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT * @hide */ @SystemApi @@ -1258,7 +1286,7 @@ public class DevicePolicyManager { * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning * trigger is cloud enrollment. * @see #PROVISIONING_TRIGGER_QR_CODE - * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER + * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT * @see #PROVISIONING_TRIGGER_UNSPECIFIED * @hide */ @@ -1269,7 +1297,7 @@ public class DevicePolicyManager { * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning * trigger is the QR code scanner. * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT - * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER + * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT * @see #PROVISIONING_TRIGGER_UNSPECIFIED * @hide */ @@ -1279,15 +1307,78 @@ public class DevicePolicyManager { /** * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning * trigger is persistent device owner enrollment. + * @deprecated Use the broader {@link #PROVISIONING_TRIGGER_MANAGED_ACCOUNT} instead * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT * @see #PROVISIONING_TRIGGER_QR_CODE * @see #PROVISIONING_TRIGGER_UNSPECIFIED * @hide */ @SystemApi + @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; /** + * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning + * trigger is managed account enrollment. + * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT + * @see #PROVISIONING_TRIGGER_QR_CODE + * @see #PROVISIONING_TRIGGER_UNSPECIFIED + * @hide + */ + @SystemApi + public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; + + /** + * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is + * organization-owned. + * + * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE} + * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra + * contain {@link #PROVISIONING_MODE_MANAGED_PROFILE} and {@link + * #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}. + * + * <p>Also, if this value is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity + * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link + * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras. + * + * @hide + */ + @SystemApi + public static final int SUPPORTED_MODES_ORGANIZATION_OWNED = 1; + + /** + * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is + * personally-owned. + * + * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE} + * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra + * contain only {@link #PROVISIONING_MODE_MANAGED_PROFILE}. + * + * @hide + */ + @SystemApi + public static final int SUPPORTED_MODES_PERSONALLY_OWNED = 2; + + /** + * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning could + * be organization-owned or personally-owned. + * + * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE} + * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra + * contain {@link + * #PROVISIONING_MODE_MANAGED_PROFILE}, {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE} and + * {@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE}. + * + * <p>Also, if this value is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity + * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link + * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras. + * + * @hide + */ + @SystemApi + public static final int SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED = 3; + + /** * This MIME type is used for starting the device owner provisioning. * * <p>During device owner provisioning a device admin app is set as the owner of the device. @@ -2380,16 +2471,57 @@ public class DevicePolicyManager { /** * An intent extra holding the provisioning mode returned by the administrator. - * The value for this extra should be one of the following: - * <ul> - * <li>{@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}</li> - * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li> - * </ul> + * The value of this extra must be one of the values provided in {@link + * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES}, which is provided as an intent extra to + * the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity. + * + * @see #PROVISIONING_MODE_FULLY_MANAGED_DEVICE + * @see #PROVISIONING_MODE_MANAGED_PROFILE */ public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE"; /** + * An integer extra indication what provisioning modes should be available for the admin app + * to pick. + * + * <p>The default value is {@link #SUPPORTED_MODES_ORGANIZATION_OWNED}. + * + * <p>The value of this extra will determine the contents of the {@link + * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array that is passed to the admin app as an + * extra to its {@link #ACTION_GET_PROVISIONING_MODE} activity. + * + * <p>If one of the possible admin app choices is a personally-owned work profile, then the + * IMEI and serial number will not be passed to the admin app's {@link + * #ACTION_GET_PROVISIONING_MODE} activity via the {@link #EXTRA_PROVISIONING_IMEI} and {@link + * #EXTRA_PROVISIONING_SERIAL_NUMBER} respectively. + * + * <p>This extra is only respected when provided alongside the {@link + * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent action. + * + * @see #SUPPORTED_MODES_ORGANIZATION_OWNED + * @see #SUPPORTED_MODES_PERSONALLY_OWNED + * @see #SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED + * @hide + */ + @SystemApi + public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = + "android.app.extra.PROVISIONING_SUPPORTED_MODES"; + + /** + * An {@link ArrayList} of {@link Integer} extra specifying the allowed provisioning modes. + * <p>This extra will be passed to the admin app's {@link #ACTION_GET_PROVISIONING_MODE} + * activity, whose result intent must contain {@link #EXTRA_PROVISIONING_MODE} set to one of + * the values in this array. + * <p>If the value set to {@link #EXTRA_PROVISIONING_MODE} is not in the array, + * provisioning will fail. + * @see #PROVISIONING_MODE_MANAGED_PROFILE + * @see #PROVISIONING_MODE_FULLY_MANAGED_DEVICE + */ + public static final String EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES = + "android.app.extra.PROVISIONING_ALLOWED_PROVISIONING_MODES"; + + /** * The provisioning mode for fully managed device. */ public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1; @@ -2400,6 +2532,11 @@ public class DevicePolicyManager { public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; /** + * The provisioning mode for a work profile on a personal device. + */ + public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE = 3; + + /** * Activity action: Starts the administrator to show policy compliance for the provisioning. * This action is used any time that the administrator has an opportunity to show policy * compliance before the end of setup wizard. This could happen as part of the admin-integrated diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java index aa94e817c152..40ae1f0c11ea 100644 --- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java +++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java @@ -26,10 +26,10 @@ import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -201,10 +201,10 @@ public final class FactoryResetProtectionPolicy implements Parcelable { * @hide */ @Nullable - public static FactoryResetProtectionPolicy readFromXml(@NonNull XmlPullParser parser) { + public static FactoryResetProtectionPolicy readFromXml(@NonNull TypedXmlPullParser parser) { try { - boolean factoryResetProtectionEnabled = Boolean.parseBoolean( - parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_ENABLED)); + boolean factoryResetProtectionEnabled = parser.getAttributeBoolean(null, + KEY_FACTORY_RESET_PROTECTION_ENABLED, false); List<String> factoryResetProtectionAccounts = new ArrayList<>(); int outerDepth = parser.getDepth(); @@ -232,9 +232,9 @@ public final class FactoryResetProtectionPolicy implements Parcelable { /** * @hide */ - public void writeToXml(@NonNull XmlSerializer out) throws IOException { - out.attribute(null, KEY_FACTORY_RESET_PROTECTION_ENABLED, - Boolean.toString(mFactoryResetProtectionEnabled)); + public void writeToXml(@NonNull TypedXmlSerializer out) throws IOException { + out.attributeBoolean(null, KEY_FACTORY_RESET_PROTECTION_ENABLED, + mFactoryResetProtectionEnabled); for (String account : mFactoryResetProtectionAccounts) { out.startTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT); out.attribute(null, ATTR_VALUE, account); diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java index 53b238633fec..b88bf76c96ca 100644 --- a/core/java/android/app/admin/SystemUpdateInfo.java +++ b/core/java/android/app/admin/SystemUpdateInfo.java @@ -21,9 +21,11 @@ import android.annotation.Nullable; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; +import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; @@ -34,6 +36,7 @@ import java.util.Objects; * A class containing information about a pending system update. */ public final class SystemUpdateInfo implements Parcelable { + private static final String TAG = "SystemUpdateInfo"; /** * Represents it is unknown whether the system update is a security patch. @@ -125,27 +128,32 @@ public final class SystemUpdateInfo implements Parcelable { }; /** @hide */ - public void writeToXml(XmlSerializer out, String tag) throws IOException { + public void writeToXml(TypedXmlSerializer out, String tag) throws IOException { out.startTag(null, tag); - out.attribute(null, ATTR_RECEIVED_TIME, String.valueOf(mReceivedTime)); - out.attribute(null, ATTR_SECURITY_PATCH_STATE, String.valueOf(mSecurityPatchState)); + out.attributeLong(null, ATTR_RECEIVED_TIME, mReceivedTime); + out.attributeInt(null, ATTR_SECURITY_PATCH_STATE, mSecurityPatchState); out.attribute(null, ATTR_ORIGINAL_BUILD , Build.FINGERPRINT); out.endTag(null, tag); } /** @hide */ @Nullable - public static SystemUpdateInfo readFromXml(XmlPullParser parser) { + public static SystemUpdateInfo readFromXml(TypedXmlPullParser parser) { // If an OTA has been applied (build fingerprint has changed), discard stale info. final String buildFingerprint = parser.getAttributeValue(null, ATTR_ORIGINAL_BUILD ); if (!Build.FINGERPRINT.equals(buildFingerprint)) { return null; } - final long receivedTime = - Long.parseLong(parser.getAttributeValue(null, ATTR_RECEIVED_TIME)); - final int securityPatchState = - Integer.parseInt(parser.getAttributeValue(null, ATTR_SECURITY_PATCH_STATE)); - return new SystemUpdateInfo(receivedTime, securityPatchState); + try { + final long receivedTime = + parser.getAttributeLong(null, ATTR_RECEIVED_TIME); + final int securityPatchState = + parser.getAttributeInt(null, ATTR_SECURITY_PATCH_STATE); + return new SystemUpdateInfo(receivedTime, securityPatchState); + } catch (XmlPullParserException e) { + Log.w(TAG, "Load xml failed", e); + return null; + } } @Override diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java index 2ba2c0425b2f..68ac4ccf210a 100644 --- a/core/java/android/app/admin/SystemUpdatePolicy.java +++ b/core/java/android/app/admin/SystemUpdatePolicy.java @@ -26,10 +26,10 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Log; import android.util.Pair; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.lang.annotation.Retention; @@ -741,38 +741,31 @@ public final class SystemUpdatePolicy implements Parcelable { * system server from a validated policy object previously. * @hide */ - public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) { + public static SystemUpdatePolicy restoreFromXml(TypedXmlPullParser parser) { try { SystemUpdatePolicy policy = new SystemUpdatePolicy(); - String value = parser.getAttributeValue(null, KEY_POLICY_TYPE); - if (value != null) { - policy.mPolicyType = Integer.parseInt(value); - - value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_START); - if (value != null) { - policy.mMaintenanceWindowStart = Integer.parseInt(value); - } - value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_END); - if (value != null) { - policy.mMaintenanceWindowEnd = Integer.parseInt(value); + policy.mPolicyType = + parser.getAttributeInt(null, KEY_POLICY_TYPE, TYPE_UNKNOWN); + policy.mMaintenanceWindowStart = + parser.getAttributeInt(null, KEY_INSTALL_WINDOW_START, 0); + policy.mMaintenanceWindowEnd = + parser.getAttributeInt(null, KEY_INSTALL_WINDOW_END, 0); + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != END_DOCUMENT + && (type != END_TAG || parser.getDepth() > outerDepth)) { + if (type == END_TAG || type == TEXT) { + continue; } - - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != END_DOCUMENT - && (type != END_TAG || parser.getDepth() > outerDepth)) { - if (type == END_TAG || type == TEXT) { - continue; - } - if (!parser.getName().equals(KEY_FREEZE_TAG)) { - continue; - } - policy.mFreezePeriods.add(new FreezePeriod( - MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_START)), - MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_END)))); + if (!parser.getName().equals(KEY_FREEZE_TAG)) { + continue; } - return policy; + policy.mFreezePeriods.add(new FreezePeriod( + MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_START)), + MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_END)))); } + return policy; } catch (NumberFormatException | XmlPullParserException | IOException e) { // Fail through Log.w(TAG, "Load xml failed", e); @@ -783,10 +776,10 @@ public final class SystemUpdatePolicy implements Parcelable { /** * @hide */ - public void saveToXml(XmlSerializer out) throws IOException { - out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType)); - out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart)); - out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd)); + public void saveToXml(TypedXmlSerializer out) throws IOException { + out.attributeInt(null, KEY_POLICY_TYPE, mPolicyType); + out.attributeInt(null, KEY_INSTALL_WINDOW_START, mMaintenanceWindowStart); + out.attributeInt(null, KEY_INSTALL_WINDOW_END, mMaintenanceWindowEnd); for (int i = 0; i < mFreezePeriods.size(); i++) { FreezePeriod interval = mFreezePeriods.get(i); out.startTag(null, KEY_FREEZE_TAG); diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 44a4b78e1afa..673de8fa7c8c 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -205,13 +205,16 @@ public class BackupManager { @Retention(RetentionPolicy.SOURCE) @IntDef({ OperationType.BACKUP, - OperationType.MIGRATION + OperationType.MIGRATION, + OperationType.ADB_BACKUP, }) public @interface OperationType { - // A regular backup / restore operation. + // A backup / restore to / from an off-device location, e.g. cloud. int BACKUP = 0; - // A full migration: all app data for non-system apps is eligible. + // A direct transfer to another device. int MIGRATION = 1; + // Backup via adb, data saved on the host machine. + int ADB_BACKUP = 3; } private Context mContext; diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl index 6d790b381ace..5fc25f0422e2 100644 --- a/core/java/android/app/role/IRoleManager.aidl +++ b/core/java/android/app/role/IRoleManager.aidl @@ -53,5 +53,9 @@ interface IRoleManager { List<String> getHeldRolesFromController(in String packageName); - String getDefaultSmsPackage(int userId); + String getBrowserRoleHolder(int userId); + + boolean setBrowserRoleHolder(String packageName, int userId); + + String getSmsRoleHolder(int userId); } diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index 408ce0f2ab1a..8b2e07b09701 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -613,12 +613,56 @@ public final class RoleManager { } /** - * Allows getting the role holder for {@link #ROLE_SMS} without - * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as required by - * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)} + * Get the role holder of {@link #ROLE_BROWSER} without requiring + * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in + * {@link android.content.pm.PackageManager#getDefaultBrowserPackageNameAsUser(int)} + * + * @param userId the user ID + * @return the package name of the default browser, or {@code null} if none + * + * @hide + */ + @Nullable + //@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public String getBrowserRoleHolder(@UserIdInt int userId) { + try { + return mService.getBrowserRoleHolder(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set the role holder of {@link #ROLE_BROWSER} requiring + * {@link Manifest.permission.SET_PREFERRED_APPLICATIONS} instead of + * {@link Manifest.permission#MANAGE_ROLE_HOLDERS}, as in + * {@link android.content.pm.PackageManager#setDefaultBrowserPackageNameAsUser(String, int)} + * + * @param packageName the package name of the default browser, or {@code null} if none + * @param userId the user ID + * @return whether the default browser was set successfully + * + * @hide + */ + @Nullable + @RequiresPermission(Manifest.permission.SET_PREFERRED_APPLICATIONS) + //@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) { + try { + return mService.setBrowserRoleHolder(packageName, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allows getting the role holder for {@link #ROLE_SMS} without requiring + * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in + * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}. + * + * @param userId the user ID to get the default SMS package for + * @return the package name of the default SMS app, or {@code null} if none * - * @param userId The user ID to get the default SMS package for. - * @return the package name of the default SMS app, or {@code null} if not configured. * @hide */ @Nullable @@ -626,7 +670,7 @@ public final class RoleManager { @TestApi public String getSmsRoleHolder(@UserIdInt int userId) { try { - return mService.getDefaultSmsPackage(userId); + return mService.getSmsRoleHolder(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java index f7c645e7cb38..813e0f93a1f7 100644 --- a/core/java/android/app/servertransaction/PauseActivityItem.java +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -20,12 +20,11 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityTaskManager; +import android.app.ActivityClient; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.IBinder; import android.os.Parcel; -import android.os.RemoteException; import android.os.Trace; /** @@ -61,12 +60,8 @@ public class PauseActivityItem extends ActivityLifecycleItem { if (mDontReport) { return; } - try { - // TODO(lifecycler): Use interface callback instead of AMS. - ActivityTaskManager.getService().activityPaused(token); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + // TODO(lifecycler): Use interface callback instead of actual implementation. + ActivityClient.getInstance().activityPaused(token); } diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java index 52ba8fb73f5f..a47fe821cd01 100644 --- a/core/java/android/app/servertransaction/PendingTransactionActions.java +++ b/core/java/android/app/servertransaction/PendingTransactionActions.java @@ -18,13 +18,11 @@ package android.app.servertransaction; import static android.app.ActivityThread.DEBUG_MEMORY_TRIM; -import android.app.ActivityManager; -import android.app.ActivityTaskManager; +import android.app.ActivityClient; import android.app.ActivityThread.ActivityClientRecord; import android.os.Build; import android.os.Bundle; import android.os.PersistableBundle; -import android.os.RemoteException; import android.os.TransactionTooLargeException; import android.util.Log; import android.util.LogWriter; @@ -142,9 +140,9 @@ public class PendingTransactionActions { try { if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity); // TODO(lifecycler): Use interface callback instead of AMS. - ActivityTaskManager.getService().activityStopped( + ActivityClient.getInstance().activityStopped( mActivity.token, mState, mPersistentState, mDescription); - } catch (RemoteException ex) { + } catch (RuntimeException ex) { // Dump statistics about bundle to help developers debug final LogWriter writer = new LogWriter(Log.WARN, TAG); final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); @@ -153,12 +151,12 @@ public class PendingTransactionActions { pw.println("PersistableBundle stats:"); Bundle.dumpStats(pw, mPersistentState); - if (ex instanceof TransactionTooLargeException + if (ex.getCause() instanceof TransactionTooLargeException && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) { Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex); return; } - throw ex.rethrowFromSystemServer(); + throw ex; } } } diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java index b4523f581ba8..d451599cc7b0 100644 --- a/core/java/android/app/servertransaction/ResumeActivityItem.java +++ b/core/java/android/app/servertransaction/ResumeActivityItem.java @@ -20,13 +20,12 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityClient; import android.app.ActivityManager; -import android.app.ActivityTaskManager; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.IBinder; import android.os.Parcel; -import android.os.RemoteException; import android.os.Trace; /** @@ -60,12 +59,8 @@ public class ResumeActivityItem extends ActivityLifecycleItem { @Override public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { - try { - // TODO(lifecycler): Use interface callback instead of AMS. - ActivityTaskManager.getService().activityResumed(token); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + // TODO(lifecycler): Use interface callback instead of actual implementation. + ActivityClient.getInstance().activityResumed(token); } @Override diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java index 2b0c1b9869e0..5cd3d68f0326 100644 --- a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java +++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java @@ -19,12 +19,11 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityTaskManager; +import android.app.ActivityClient; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.IBinder; import android.os.Parcel; -import android.os.RemoteException; import android.os.Trace; /** @@ -56,11 +55,7 @@ public class TopResumedActivityChangeItem extends ActivityTransactionItem { // 2. Activity wasn't RESUMED yet, which means that it didn't receive the top state yet. // 3. Activity is PAUSED or in other lifecycle state after PAUSED. In this case top resumed // state loss was already called right before pausing. - try { - ActivityTaskManager.getService().activityTopResumedStateLost(); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + ActivityClient.getInstance().activityTopResumedStateLost(); } diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 3522b1b8aff5..081f4fdc1b12 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -383,6 +383,10 @@ public final class UsageEvents implements Parcelable { public int mClassToken = UNASSIGNED_TOKEN; /** + * Uniquely identifies an activity. It's possible for two activities with the same + * pkg/class name to be in lifecycle at the same time. The mInstanceId is guaranteed to be + * unique per activity across all apps (not just within a single app). + * * {@hide} */ public int mInstanceId; diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java index 130a20dfb07b..c4333ac44e34 100644 --- a/core/java/android/appwidget/AppWidgetProviderInfo.java +++ b/core/java/android/appwidget/AppWidgetProviderInfo.java @@ -68,6 +68,9 @@ public class AppWidgetProviderInfo implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ResizeModeFlags {} + /** {@hide} */ + public static final int WIDGET_CATEGORY_UNKNOWN = -1; + /** * Indicates that the widget can be displayed on the home screen. This is the default value. */ diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java index 603a7ff29ea3..7fe18a0704ba 100755 --- a/core/java/android/bluetooth/BluetoothClass.java +++ b/core/java/android/bluetooth/BluetoothClass.java @@ -426,13 +426,13 @@ public final class BluetoothClass implements Parcelable { return false; } } else if (profile == PROFILE_HID) { - return (getDeviceClass() & Device.Major.PERIPHERAL) == Device.Major.PERIPHERAL; + return getMajorDeviceClass() == Device.Major.PERIPHERAL; } else if (profile == PROFILE_PANU || profile == PROFILE_NAP) { // No good way to distinguish between the two, based on class bits. if (hasService(Service.NETWORKING)) { return true; } - return (getDeviceClass() & Device.Major.NETWORKING) == Device.Major.NETWORKING; + return getMajorDeviceClass() == Device.Major.NETWORKING; } else { return false; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 8f92bf1e3253..d920fb3e97e6 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3544,6 +3544,7 @@ public abstract class Context { LIGHTS_SERVICE, //@hide: PEOPLE_SERVICE, //@hide: DEVICE_STATE_SERVICE, + UWB_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -5260,6 +5261,15 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.uwb.UwbManager}. + * + * @see #getSystemService(String) + * @hide + */ + public static final String UWB_SERVICE = "uwb"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a * {@link android.app.DreamManager} for controlling Dream states. * * @see #getSystemService(String) diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index b0c47bdfd864..56da3cb0eb02 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -270,7 +270,7 @@ public class ContextWrapper extends Context { } @Override - public File getExternalFilesDir(String type) { + public @Nullable File getExternalFilesDir(@Nullable String type) { return mBase.getExternalFilesDir(type); } @@ -300,7 +300,7 @@ public class ContextWrapper extends Context { } @Override - public File getExternalCacheDir() { + public @Nullable File getExternalCacheDir() { return mBase.getExternalCacheDir(); } @@ -322,7 +322,7 @@ public class ContextWrapper extends Context { /** @hide **/ @Override - public File getPreloadsFileCache() { + public @Nullable File getPreloadsFileCache() { return mBase.getPreloadsFileCache(); } @@ -333,7 +333,7 @@ public class ContextWrapper extends Context { @Override public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory, - DatabaseErrorHandler errorHandler) { + @Nullable DatabaseErrorHandler errorHandler) { return mBase.openOrCreateDatabase(name, mode, factory, errorHandler); } @@ -412,7 +412,7 @@ public class ContextWrapper extends Context { /** @hide **/ public void startActivityForResult( - String who, Intent intent, int requestCode, Bundle options) { + String who, Intent intent, int requestCode, @Nullable Bundle options) { mBase.startActivityForResult(who, intent, requestCode, options); } @@ -422,13 +422,13 @@ public class ContextWrapper extends Context { } @Override - public void startActivity(Intent intent, Bundle options) { + public void startActivity(Intent intent, @Nullable Bundle options) { mBase.startActivity(intent, options); } /** @hide */ @Override - public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) { + public void startActivityAsUser(Intent intent, @Nullable Bundle options, UserHandle user) { mBase.startActivityAsUser(intent, options, user); } @@ -438,13 +438,14 @@ public class ContextWrapper extends Context { } @Override - public void startActivities(Intent[] intents, Bundle options) { + public void startActivities(Intent[] intents, @Nullable Bundle options) { mBase.startActivities(intents, options); } /** @hide */ @Override - public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { + public int startActivitiesAsUser(Intent[] intents, @Nullable Bundle options, + UserHandle userHandle) { return mBase.startActivitiesAsUser(intents, options, userHandle); } @@ -472,7 +473,7 @@ public class ContextWrapper extends Context { } @Override - public void sendBroadcast(Intent intent, String receiverPermission) { + public void sendBroadcast(Intent intent, @Nullable String receiverPermission) { mBase.sendBroadcast(intent, receiverPermission); } @@ -493,27 +494,28 @@ public class ContextWrapper extends Context { /** @hide */ @SystemApi @Override - public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) { + public void sendBroadcast(Intent intent, @Nullable String receiverPermission, + @Nullable Bundle options) { mBase.sendBroadcast(intent, receiverPermission, options); } /** @hide */ @Override - public void sendBroadcast(Intent intent, String receiverPermission, int appOp) { + public void sendBroadcast(Intent intent, @Nullable String receiverPermission, int appOp) { mBase.sendBroadcast(intent, receiverPermission, appOp); } @Override public void sendOrderedBroadcast(Intent intent, - String receiverPermission) { + @Nullable String receiverPermission) { mBase.sendOrderedBroadcast(intent, receiverPermission); } @Override public void sendOrderedBroadcast( - Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, - Bundle initialExtras) { + Intent intent, @Nullable String receiverPermission, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras); @@ -523,10 +525,9 @@ public class ContextWrapper extends Context { @SystemApi @Override public void sendOrderedBroadcast( - Intent intent, String receiverPermission, Bundle options, - BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, - Bundle initialExtras) { + Intent intent, @Nullable String receiverPermission, @Nullable Bundle options, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcast(intent, receiverPermission, options, resultReceiver, scheduler, initialCode, initialData, initialExtras); @@ -535,9 +536,9 @@ public class ContextWrapper extends Context { /** @hide */ @Override public void sendOrderedBroadcast( - Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, - Bundle initialExtras) { + Intent intent, @Nullable String receiverPermission, int appOp, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcast(intent, receiverPermission, appOp, resultReceiver, scheduler, initialCode, initialData, initialExtras); @@ -557,21 +558,22 @@ public class ContextWrapper extends Context { /** @hide */ @Override public void sendBroadcastAsUser(Intent intent, UserHandle user, - String receiverPermission, Bundle options) { + @Nullable String receiverPermission, @Nullable Bundle options) { mBase.sendBroadcastAsUser(intent, user, receiverPermission, options); } /** @hide */ @Override public void sendBroadcastAsUser(Intent intent, UserHandle user, - String receiverPermission, int appOp) { + @Nullable String receiverPermission, int appOp) { mBase.sendBroadcastAsUser(intent, user, receiverPermission, appOp); } @Override public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, - String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, - int initialCode, String initialData, Bundle initialExtras) { + @Nullable String receiverPermission, @Nullable BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras); } @@ -579,8 +581,9 @@ public class ContextWrapper extends Context { /** @hide */ @Override public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, - String receiverPermission, int appOp, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + @Nullable String receiverPermission, int appOp, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, resultReceiver, scheduler, initialCode, initialData, initialExtras); } @@ -588,8 +591,9 @@ public class ContextWrapper extends Context { /** @hide */ @Override public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, - String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + @Nullable String receiverPermission, int appOp, @Nullable Bundle options, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, options, resultReceiver, scheduler, initialCode, initialData, initialExtras); } @@ -621,10 +625,9 @@ public class ContextWrapper extends Context { @Override @Deprecated - public void sendStickyOrderedBroadcast( - Intent intent, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, - Bundle initialExtras) { + public void sendStickyOrderedBroadcast(Intent intent, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode, initialData, initialExtras); @@ -645,16 +648,17 @@ public class ContextWrapper extends Context { /** @hide */ @Override @Deprecated - public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) { + public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, + @Nullable Bundle options) { mBase.sendStickyBroadcastAsUser(intent, user, options); } @Override @Deprecated public void sendStickyOrderedBroadcastAsUser(Intent intent, - UserHandle user, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, - Bundle initialExtras) { + UserHandle user, @Nullable BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras) { mBase.sendStickyOrderedBroadcastAsUser(intent, user, resultReceiver, scheduler, initialCode, initialData, initialExtras); } @@ -666,29 +670,26 @@ public class ContextWrapper extends Context { } @Override - public Intent registerReceiver( - BroadcastReceiver receiver, IntentFilter filter) { + public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter) { return mBase.registerReceiver(receiver, filter); } @Override - public Intent registerReceiver( - BroadcastReceiver receiver, IntentFilter filter, int flags) { + public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter, + int flags) { return mBase.registerReceiver(receiver, filter, flags); } @Override - public Intent registerReceiver( - BroadcastReceiver receiver, IntentFilter filter, - String broadcastPermission, Handler scheduler) { + public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter, + @Nullable String broadcastPermission, @Nullable Handler scheduler) { return mBase.registerReceiver(receiver, filter, broadcastPermission, scheduler); } @Override - public Intent registerReceiver( - BroadcastReceiver receiver, IntentFilter filter, - String broadcastPermission, Handler scheduler, int flags) { + public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter, + @Nullable String broadcastPermission, @Nullable Handler scheduler, int flags) { return mBase.registerReceiver(receiver, filter, broadcastPermission, scheduler, flags); } @@ -706,9 +707,9 @@ public class ContextWrapper extends Context { /** @hide */ @Override @UnsupportedAppUsage - public Intent registerReceiverAsUser( - BroadcastReceiver receiver, UserHandle user, IntentFilter filter, - String broadcastPermission, Handler scheduler) { + public Intent registerReceiverAsUser(@Nullable BroadcastReceiver receiver, UserHandle user, + IntentFilter filter, @Nullable String broadcastPermission, + @Nullable Handler scheduler) { return mBase.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler); } @@ -719,12 +720,12 @@ public class ContextWrapper extends Context { } @Override - public ComponentName startService(Intent service) { + public @Nullable ComponentName startService(Intent service) { return mBase.startService(service); } @Override - public ComponentName startForegroundService(Intent service) { + public @Nullable ComponentName startForegroundService(Intent service) { return mBase.startForegroundService(service); } @@ -736,14 +737,14 @@ public class ContextWrapper extends Context { /** @hide */ @Override @UnsupportedAppUsage - public ComponentName startServiceAsUser(Intent service, UserHandle user) { + public @Nullable ComponentName startServiceAsUser(Intent service, UserHandle user) { return mBase.startServiceAsUser(service, user); } /** @hide */ @Override @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { + public @Nullable ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { return mBase.startForegroundServiceAsUser(service, user); } @@ -797,12 +798,12 @@ public class ContextWrapper extends Context { @Override public boolean startInstrumentation(ComponentName className, - String profileFile, Bundle arguments) { + @Nullable String profileFile, @Nullable Bundle arguments) { return mBase.startInstrumentation(className, profileFile, arguments); } @Override - public Object getSystemService(String name) { + public @Nullable Object getSystemService(String name) { return mBase.getSystemService(name); } @@ -839,18 +840,18 @@ public class ContextWrapper extends Context { @Override public void enforcePermission( - String permission, int pid, int uid, String message) { + String permission, int pid, int uid, @Nullable String message) { mBase.enforcePermission(permission, pid, uid, message); } @Override - public void enforceCallingPermission(String permission, String message) { + public void enforceCallingPermission(String permission, @Nullable String message) { mBase.enforceCallingPermission(permission, message); } @Override public void enforceCallingOrSelfPermission( - String permission, String message) { + String permission, @Nullable String message) { mBase.enforceCallingOrSelfPermission(permission, message); } @@ -891,8 +892,8 @@ public class ContextWrapper extends Context { } @Override - public int checkUriPermission(Uri uri, String readPermission, - String writePermission, int pid, int uid, int modeFlags) { + public int checkUriPermission(@Nullable Uri uri, @Nullable String readPermission, + @Nullable String writePermission, int pid, int uid, int modeFlags) { return mBase.checkUriPermission(uri, readPermission, writePermission, pid, uid, modeFlags); } @@ -917,8 +918,8 @@ public class ContextWrapper extends Context { @Override public void enforceUriPermission( - Uri uri, String readPermission, String writePermission, - int pid, int uid, int modeFlags, String message) { + @Nullable Uri uri, @Nullable String readPermission, @Nullable String writePermission, + int pid, int uid, int modeFlags, @Nullable String message) { mBase.enforceUriPermission( uri, readPermission, writePermission, pid, uid, modeFlags, message); @@ -1063,7 +1064,7 @@ public class ContextWrapper extends Context { * @hide */ @Override - public IBinder getActivityToken() { + public @Nullable IBinder getActivityToken() { return mBase.getActivityToken(); } @@ -1071,7 +1072,7 @@ public class ContextWrapper extends Context { * @hide */ @Override - public IBinder getWindowContextToken() { + public @Nullable IBinder getWindowContextToken() { return mBase != null ? mBase.getWindowContextToken() : null; } @@ -1079,8 +1080,8 @@ public class ContextWrapper extends Context { * @hide */ @Override - public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler, - int flags) { + public @Nullable IServiceConnection getServiceDispatcher(ServiceConnection conn, + Handler handler, int flags) { return mBase.getServiceDispatcher(conn, handler, flags); } @@ -1142,7 +1143,7 @@ public class ContextWrapper extends Context { * @hide */ @Override - public ContentCaptureOptions getContentCaptureOptions() { + public @Nullable ContentCaptureOptions getContentCaptureOptions() { return mBase == null ? null : mBase.getContentCaptureOptions(); } @@ -1151,7 +1152,7 @@ public class ContextWrapper extends Context { */ @TestApi @Override - public void setContentCaptureOptions(ContentCaptureOptions options) { + public void setContentCaptureOptions(@Nullable ContentCaptureOptions options) { if (mBase != null) { mBase.setContentCaptureOptions(options); } diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java index 58445a7f9242..495f94f98f95 100644 --- a/core/java/android/content/SyncAdaptersCache.java +++ b/core/java/android/content/SyncAdaptersCache.java @@ -25,12 +25,12 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.annotations.GuardedBy; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -142,12 +142,12 @@ public class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> } static class MySerializer implements XmlSerializerAndParser<SyncAdapterType> { - public void writeAsXml(SyncAdapterType item, XmlSerializer out) throws IOException { + public void writeAsXml(SyncAdapterType item, TypedXmlSerializer out) throws IOException { out.attribute(null, "authority", item.authority); out.attribute(null, "accountType", item.accountType); } - public SyncAdapterType createFromXml(XmlPullParser parser) + public SyncAdapterType createFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final String authority = parser.getAttributeValue(null, "authority"); final String accountType = parser.getAttributeValue(null, "accountType"); diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl index 6ccbc36e26f6..9a73be9d44a4 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -33,6 +33,7 @@ interface IPackageInstallerSession { ParcelFileDescriptor openRead(String name); void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd); + void stageViaHardLink(String target); void addChecksums(String name, in Checksum[] checksums); diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java index 0a913acba9f5..56b8bd80a06a 100644 --- a/core/java/android/content/pm/IntentFilterVerificationInfo.java +++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java @@ -16,11 +16,11 @@ package android.content.pm; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; import android.annotation.SystemApi; import android.os.Parcel; @@ -28,12 +28,13 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -72,7 +73,7 @@ public final class IntentFilterVerificationInfo implements Parcelable { } /** @hide */ - public IntentFilterVerificationInfo(XmlPullParser parser) + public IntentFilterVerificationInfo(TypedXmlPullParser parser) throws IOException, XmlPullParserException { readFromXml(parser); } @@ -121,7 +122,7 @@ public final class IntentFilterVerificationInfo implements Parcelable { return sb.toString(); } - String getStringFromXml(XmlPullParser parser, String attribute, String defaultValue) { + String getStringFromXml(TypedXmlPullParser parser, String attribute, String defaultValue) { String value = parser.getAttributeValue(null, attribute); if (value == null) { String msg = "Missing element under " + TAG +": " + attribute + " at " + @@ -133,20 +134,12 @@ public final class IntentFilterVerificationInfo implements Parcelable { } } - int getIntFromXml(XmlPullParser parser, String attribute, int defaultValue) { - String value = parser.getAttributeValue(null, attribute); - if (TextUtils.isEmpty(value)) { - String msg = "Missing element under " + TAG +": " + attribute + " at " + - parser.getPositionDescription(); - Log.w(TAG, msg); - return defaultValue; - } else { - return Integer.parseInt(value); - } + int getIntFromXml(TypedXmlPullParser parser, String attribute, int defaultValue) { + return parser.getAttributeInt(null, attribute, defaultValue); } /** @hide */ - public void readFromXml(XmlPullParser parser) throws XmlPullParserException, + public void readFromXml(TypedXmlPullParser parser) throws XmlPullParserException, IOException { mPackageName = getStringFromXml(parser, ATTR_PACKAGE_NAME, null); if (mPackageName == null) { @@ -182,9 +175,9 @@ public final class IntentFilterVerificationInfo implements Parcelable { } /** @hide */ - public void writeToXml(XmlSerializer serializer) throws IOException { + public void writeToXml(TypedXmlSerializer serializer) throws IOException { serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName); - serializer.attribute(null, ATTR_STATUS, String.valueOf(mStatus)); + serializer.attributeInt(null, ATTR_STATUS, mStatus); for (String str : mDomains) { serializer.startTag(null, TAG_DOMAIN); serializer.attribute(null, ATTR_DOMAIN_NAME, str); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 0dcfd38294b9..d4a98f82a4f7 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1049,6 +1049,31 @@ public class PackageInstaller { } /** + * Populate an APK file by creating a hard link to avoid the need to copy. + * <p> + * Note this API is used by RollbackManager only and can only be called from system_server. + * {@code target} will be relabeled if link is created successfully. RollbackManager has + * to delete {@code target} when the session is committed successfully to avoid SELinux + * label conflicts. + * <p> + * Note No more bytes should be written to the file once the link is created successfully. + * + * @param target the path of the link target + * + * @hide + */ + public void stageViaHardLink(String target) throws IOException { + try { + mSession.stageViaHardLink(target); + } catch (RuntimeException e) { + ExceptionUtils.maybeUnwrapIOException(e); + throw e; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Ensure that any outstanding data for given stream has been committed * to disk. This is only valid for streams returned from * {@link #openWrite(String, long, long)}. diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 044b3b2e8284..d550c7c68033 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -98,6 +98,11 @@ import java.util.Set; * packages that are currently installed on the device. * * You can find this class through {@link Context#getPackageManager}. + * + * <p class="note"><strong>Note: </strong>If your app targets Android 11 (API level 30) or + * higher, the methods in this class each return a filtered list of apps. Learn more about how to + * <a href="/training/basics/intents/package-visibility">manage package visibility</a>. + * </p> */ public abstract class PackageManager { private static final String TAG = "PackageManager"; @@ -2810,6 +2815,15 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device is capable of communicating with + * other devices via ultra wideband. + * @hide + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_UWB = "android.hardware.uwb"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports connecting to USB devices * as the USB host. */ @@ -4117,6 +4131,14 @@ public abstract class PackageManager { */ public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; + /** + * A manifest property to control app's participation in {@code adb backup}. Should only + * be used by system / privileged apps. + * + * @hide + */ + public static final String PROPERTY_ALLOW_ADB_BACKUP = "android.backup.ALLOW_ADB_BACKUP"; + /** {@hide} */ public int getUserId() { return UserHandle.myUserId(); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index cb0decfa4982..4dfbd75a9d67 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -406,9 +406,15 @@ public class PackageParser { public final boolean extractNativeLibs; public final boolean isolatedSplits; - public PackageLite(String codePath, ApkLite baseApk, String[] splitNames, - boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit, - String[] splitCodePaths, int[] splitRevisionCodes) { + // This does not represent the actual manifest structure since the 'profilable' tag + // could be used with attributes other than 'shell'. Extend if necessary. + public final boolean profilableByShell; + public final boolean isSplitRequired; + public final boolean useEmbeddedDex; + + public PackageLite(String codePath, String baseCodePath, ApkLite baseApk, + String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames, + String[] configForSplit, String[] splitCodePaths, int[] splitRevisionCodes) { this.packageName = baseApk.packageName; this.versionCode = baseApk.versionCode; this.versionCodeMajor = baseApk.versionCodeMajor; @@ -418,8 +424,10 @@ public class PackageParser { this.isFeatureSplits = isFeatureSplits; this.usesSplitNames = usesSplitNames; this.configForSplit = configForSplit; + // The following paths may be different from the path in ApkLite because we + // move or rename the APK files. Use parameters to indicate the correct paths. this.codePath = codePath; - this.baseCodePath = baseApk.codePath; + this.baseCodePath = baseCodePath; this.splitCodePaths = splitCodePaths; this.baseRevisionCode = baseApk.revisionCode; this.splitRevisionCodes = splitRevisionCodes; @@ -429,6 +437,9 @@ public class PackageParser { this.use32bitAbi = baseApk.use32bitAbi; this.extractNativeLibs = baseApk.extractNativeLibs; this.isolatedSplits = baseApk.isolatedSplits; + this.useEmbeddedDex = baseApk.useEmbeddedDex; + this.isSplitRequired = baseApk.isSplitRequired; + this.profilableByShell = baseApk.profilableByShell; } public List<String> getAllCodePaths() { @@ -439,6 +450,10 @@ public class PackageParser { } return paths; } + + public long getLongVersionCode() { + return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode); + } } /** @@ -941,7 +956,8 @@ public class PackageParser { final ApkLite baseApk = parseApkLite(packageFile, flags); final String packagePath = packageFile.getAbsolutePath(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - return new PackageLite(packagePath, baseApk, null, null, null, null, null, null); + return new PackageLite(packagePath, baseApk.codePath, baseApk, null, null, null, null, null, + null); } static PackageLite parseClusterPackageLite(File packageDir, int flags) @@ -1031,8 +1047,8 @@ public class PackageParser { } final String codePath = packageDir.getAbsolutePath(); - return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames, - configForSplits, splitCodePaths, splitRevisionCodes); + return new PackageLite(codePath, baseApk.codePath, baseApk, splitNames, isFeatureSplits, + usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes); } /** diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 2fca980e764c..99258712030c 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -41,6 +41,8 @@ import android.util.ArraySet; import android.util.DebugUtils; import android.util.Pair; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -560,7 +562,7 @@ public class PackageUserState { * @param out the {@link XmlSerializer} object * @throws IOException */ - public void saveToXml(XmlSerializer out) throws IOException { + public void saveToXml(TypedXmlSerializer out) throws IOException { if (dialogInfo != null) { out.startTag(null, TAG_DIALOG_INFO); dialogInfo.saveToXml(out); @@ -594,7 +596,7 @@ public class PackageUserState { * @param in the reader * @return */ - public static SuspendParams restoreFromXml(XmlPullParser in) throws IOException { + public static SuspendParams restoreFromXml(TypedXmlPullParser in) throws IOException { SuspendDialogInfo readDialogInfo = null; PersistableBundle readAppExtras = null; PersistableBundle readLauncherExtras = null; diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 192470e964e0..7ecb11248d23 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -36,21 +36,21 @@ import android.util.IntArray; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastXmlSerializer; + +import libcore.io.IoUtils; import com.google.android.collect.Lists; import com.google.android.collect.Maps; -import libcore.io.IoUtils; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; @@ -58,7 +58,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -672,8 +671,7 @@ public abstract class RegisteredServicesCache<V> { */ private void readPersistentServicesLocked(InputStream is) throws XmlPullParserException, IOException { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(is, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(is); int eventType = parser.getEventType(); while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { @@ -690,8 +688,7 @@ public abstract class RegisteredServicesCache<V> { if (service == null) { break; } - String uidString = parser.getAttributeValue(null, "uid"); - final int uid = Integer.parseInt(uidString); + final int uid = parser.getAttributeInt(null, "uid"); final int userId = UserHandle.getUserId(uid); final UserServices<V> user = findOrCreateUserLocked(userId, false /*loadFromFileIfNew*/) ; @@ -762,14 +759,13 @@ public abstract class RegisteredServicesCache<V> { FileOutputStream fos = null; try { fos = atomicFile.startWrite(); - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(fos, StandardCharsets.UTF_8.name()); + TypedXmlSerializer out = Xml.resolveSerializer(fos); out.startDocument(null, true); out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "services"); for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) { out.startTag(null, "service"); - out.attribute(null, "uid", Integer.toString(service.getValue())); + out.attributeInt(null, "uid", service.getValue()); mSerializerAndParser.writeAsXml(service.getKey(), out); out.endTag(null, "service"); } diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java index 851a08116f56..60f321883e98 100644 --- a/core/java/android/content/pm/SuspendDialogInfo.java +++ b/core/java/android/content/pm/SuspendDialogInfo.java @@ -29,13 +29,12 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; - import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -169,38 +168,38 @@ public final class SuspendDialogInfo implements Parcelable { /** * @hide */ - public void saveToXml(XmlSerializer out) throws IOException { + public void saveToXml(TypedXmlSerializer out) throws IOException { if (mIconResId != ID_NULL) { - XmlUtils.writeIntAttribute(out, XML_ATTR_ICON_RES_ID, mIconResId); + out.attributeInt(null, XML_ATTR_ICON_RES_ID, mIconResId); } if (mTitleResId != ID_NULL) { - XmlUtils.writeIntAttribute(out, XML_ATTR_TITLE_RES_ID, mTitleResId); + out.attributeInt(null, XML_ATTR_TITLE_RES_ID, mTitleResId); } if (mDialogMessageResId != ID_NULL) { - XmlUtils.writeIntAttribute(out, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId); + out.attributeInt(null, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId); } else { XmlUtils.writeStringAttribute(out, XML_ATTR_DIALOG_MESSAGE, mDialogMessage); } if (mNeutralButtonTextResId != ID_NULL) { - XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId); + out.attributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId); } - XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction); + out.attributeInt(null, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction); } /** * @hide */ - public static SuspendDialogInfo restoreFromXml(XmlPullParser in) { + public static SuspendDialogInfo restoreFromXml(TypedXmlPullParser in) { final SuspendDialogInfo.Builder dialogInfoBuilder = new SuspendDialogInfo.Builder(); try { - final int iconId = XmlUtils.readIntAttribute(in, XML_ATTR_ICON_RES_ID, ID_NULL); - final int titleId = XmlUtils.readIntAttribute(in, XML_ATTR_TITLE_RES_ID, ID_NULL); - final int buttonTextId = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_TEXT_RES_ID, - ID_NULL); - final int buttonAction = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_ACTION, - BUTTON_ACTION_MORE_DETAILS); - final int dialogMessageResId = XmlUtils.readIntAttribute( - in, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL); + final int iconId = in.getAttributeInt(null, XML_ATTR_ICON_RES_ID, ID_NULL); + final int titleId = in.getAttributeInt(null, XML_ATTR_TITLE_RES_ID, ID_NULL); + final int buttonTextId = + in.getAttributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, ID_NULL); + final int buttonAction = + in.getAttributeInt(null, XML_ATTR_BUTTON_ACTION, BUTTON_ACTION_MORE_DETAILS); + final int dialogMessageResId = + in.getAttributeInt(null, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL); final String dialogMessage = XmlUtils.readStringAttribute(in, XML_ATTR_DIALOG_MESSAGE); if (iconId != ID_NULL) { diff --git a/core/java/android/content/pm/XmlSerializerAndParser.java b/core/java/android/content/pm/XmlSerializerAndParser.java index 5dce83902f78..51cd6ca60f59 100644 --- a/core/java/android/content/pm/XmlSerializerAndParser.java +++ b/core/java/android/content/pm/XmlSerializerAndParser.java @@ -17,6 +17,10 @@ package android.content.pm; import android.compat.annotation.UnsupportedAppUsage; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; + +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -26,8 +30,16 @@ import java.io.IOException; /** @hide */ public interface XmlSerializerAndParser<T> { + void writeAsXml(T item, TypedXmlSerializer out) throws IOException; + T createFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException; + @UnsupportedAppUsage - void writeAsXml(T item, XmlSerializer out) throws IOException; + default void writeAsXml(T item, XmlSerializer out) throws IOException { + writeAsXml(item, XmlUtils.makeTyped(out)); + } + @UnsupportedAppUsage - T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException; + default T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { + return createFromXml(XmlUtils.makeTyped(parser)); + } } diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index 72a66ed4d9fe..f583e2597855 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -18,9 +18,11 @@ package android.content.pm.parsing; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; +import static android.content.pm.PackageParser.APK_FILE_EXTENSION; import static android.content.pm.parsing.ParsingPackageUtils.validateName; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import android.annotation.NonNull; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser; @@ -50,6 +52,7 @@ import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** @hide */ public class ApkLiteParseUtils { @@ -95,8 +98,8 @@ public class ApkLiteParseUtils { final PackageParser.ApkLite baseApk = result.getResult(); final String packagePath = packageFile.getAbsolutePath(); return input.success( - new PackageParser.PackageLite(packagePath, baseApk, null, null, null, null, - null, null)); + new PackageParser.PackageLite(packagePath, baseApk.codePath, baseApk, null, + null, null, null, null, null)); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -159,13 +162,43 @@ public class ApkLiteParseUtils { } final PackageParser.ApkLite baseApk = apks.remove(null); + return composePackageLiteFromApks(input, packageDir, baseApk, apks); + } + + /** + * Utility method that retrieves lightweight details about the package by given location, + * base APK, and split APKs. + * + * @param packageDir Path to the package + * @param baseApk Parsed base APK + * @param splitApks Parsed split APKs + * @return PackageLite + */ + public static ParseResult<PackageParser.PackageLite> composePackageLiteFromApks( + ParseInput input, File packageDir, PackageParser.ApkLite baseApk, + ArrayMap<String, PackageParser.ApkLite> splitApks) { + return composePackageLiteFromApks(input, packageDir, baseApk, splitApks, false); + } + + /** + * Utility method that retrieves lightweight details about the package by given location, + * base APK, and split APKs. + * + * @param packageDir Path to the package + * @param baseApk Parsed base APK + * @param splitApks Parsed split APKs + * @param apkRenamed Indicate whether the APKs are renamed after parsed. + * @return PackageLite + */ + public static ParseResult<PackageParser.PackageLite> composePackageLiteFromApks( + ParseInput input, File packageDir, PackageParser.ApkLite baseApk, + ArrayMap<String, PackageParser.ApkLite> splitApks, boolean apkRenamed) { if (baseApk == null) { return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, "Missing base APK in " + packageDir); } - // Always apply deterministic ordering based on splitName - final int size = apks.size(); + final int size = ArrayUtils.size(splitApks); String[] splitNames = null; boolean[] isFeatureSplits = null; @@ -181,23 +214,39 @@ public class ApkLiteParseUtils { splitCodePaths = new String[size]; splitRevisionCodes = new int[size]; - splitNames = apks.keySet().toArray(splitNames); + splitNames = splitApks.keySet().toArray(splitNames); Arrays.sort(splitNames, PackageParser.sSplitNameComparator); for (int i = 0; i < size; i++) { - final PackageParser.ApkLite apk = apks.get(splitNames[i]); + final PackageParser.ApkLite apk = splitApks.get(splitNames[i]); usesSplitNames[i] = apk.usesSplitName; isFeatureSplits[i] = apk.isFeatureSplit; configForSplits[i] = apk.configForSplit; - splitCodePaths[i] = apk.codePath; + splitCodePaths[i] = apkRenamed ? new File(packageDir, + splitNameToFileName(apk)).getAbsolutePath() : apk.codePath; splitRevisionCodes[i] = apk.revisionCode; } } final String codePath = packageDir.getAbsolutePath(); - return input.success(new PackageParser.PackageLite(codePath, baseApk, splitNames, - isFeatureSplits, usesSplitNames, configForSplits, splitCodePaths, - splitRevisionCodes)); + final String baseCodePath = apkRenamed ? new File(packageDir, + splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.codePath; + return input.success( + new PackageParser.PackageLite(codePath, baseCodePath, baseApk, splitNames, + isFeatureSplits, usesSplitNames, configForSplits, splitCodePaths, + splitRevisionCodes)); + } + + /** + * Utility method that retrieves canonical file name by given split name from parsed APK. + * + * @param apk Parsed APK + * @return The canonical file name + */ + public static String splitNameToFileName(@NonNull PackageParser.ApkLite apk) { + Objects.requireNonNull(apk); + final String fileName = apk.splitName == null ? "base" : "split_" + apk.splitName; + return fileName + APK_FILE_EXTENSION; } /** diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl index cb43943f4864..cc12125c13f0 100644 --- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl +++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl @@ -36,7 +36,7 @@ interface IBiometricAuthenticator { // Retrieve static sensor properties SensorPropertiesInternal getSensorProperties(String opPackageName); - // Requests a proto dump of the service. See biometrics.proto + // Requests a proto dump of the sensor. See biometrics.proto byte[] dumpSensorServiceStateProto(); // This method prepares the service to start authenticating, but doesn't start authentication. diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 232056cb13d4..a0c12235dea9 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2772,7 +2772,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * </ol> * </li> * <li>Setting {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} to values different than 1.0 and - * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to be windowboxing at the same time is undefined behavior.</li> + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to be windowboxing at the same time are not supported. In this + * case, the camera framework will override the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to be the active + * array.</li> * </ul> * <p>LEGACY capability devices will only support CENTER_ONLY cropping.</p> * <p><b>Possible values:</b> diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index d931789a7005..d85bcbd15e50 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -2243,7 +2243,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * explicitly set {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, its value defaults to 1.0.</p> * <p>One limitation of controlling zoom using zoomRatio is that the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} * must only be used for letterboxing or pillarboxing of the sensor active array, and no - * FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0.</p> + * FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0. If + * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} is not 1.0, and {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is set to be + * windowboxing, the camera framework will override the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to be + * the active array.</p> * <p><b>Range of valid values:</b><br> * {@link CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE android.control.zoomRatioRange}</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index cd69788f1924..4424a71ae837 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2490,7 +2490,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * explicitly set {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, its value defaults to 1.0.</p> * <p>One limitation of controlling zoom using zoomRatio is that the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} * must only be used for letterboxing or pillarboxing of the sensor active array, and no - * FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0.</p> + * FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0. If + * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} is not 1.0, and {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is set to be + * windowboxing, the camera framework will override the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to be + * the active array.</p> * <p><b>Range of valid values:</b><br> * {@link CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE android.control.zoomRatioRange}</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java index d9c1063cd39d..366734e9bf11 100644 --- a/core/java/android/hardware/display/BrightnessConfiguration.java +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -24,13 +24,13 @@ import android.content.pm.ApplicationInfo; import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -343,15 +343,15 @@ public final class BrightnessConfiguration implements Parcelable { * * @hide */ - public void saveToXml(@NonNull XmlSerializer serializer) throws IOException { + public void saveToXml(@NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_BRIGHTNESS_CURVE); if (mDescription != null) { serializer.attribute(null, ATTR_DESCRIPTION, mDescription); } for (int i = 0; i < mLux.length; i++) { serializer.startTag(null, TAG_BRIGHTNESS_POINT); - serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i])); - serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i])); + serializer.attributeFloat(null, ATTR_LUX, mLux[i]); + serializer.attributeFloat(null, ATTR_NITS, mNits[i]); serializer.endTag(null, TAG_BRIGHTNESS_POINT); } serializer.endTag(null, TAG_BRIGHTNESS_CURVE); @@ -370,7 +370,7 @@ public final class BrightnessConfiguration implements Parcelable { final int category = entry.getKey(); final BrightnessCorrection correction = entry.getValue(); serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION); - serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category)); + serializer.attributeInt(null, ATTR_CATEGORY, category); correction.saveToXml(serializer); serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION); } @@ -378,19 +378,18 @@ public final class BrightnessConfiguration implements Parcelable { serializer.startTag(null, TAG_BRIGHTNESS_PARAMS); if (mShouldCollectColorSamples) { - serializer.attribute(null, ATTR_COLLECT_COLOR, Boolean.toString(true)); + serializer.attributeBoolean(null, ATTR_COLLECT_COLOR, true); } if (mShortTermModelTimeout >= 0) { - serializer.attribute(null, ATTR_MODEL_TIMEOUT, - Long.toString(mShortTermModelTimeout)); + serializer.attributeLong(null, ATTR_MODEL_TIMEOUT, mShortTermModelTimeout); } if (!Float.isNaN(mShortTermModelLowerLuxMultiplier)) { - serializer.attribute(null, ATTR_MODEL_LOWER_BOUND, - Float.toString(mShortTermModelLowerLuxMultiplier)); + serializer.attributeFloat(null, ATTR_MODEL_LOWER_BOUND, + mShortTermModelLowerLuxMultiplier); } if (!Float.isNaN(mShortTermModelUpperLuxMultiplier)) { - serializer.attribute(null, ATTR_MODEL_UPPER_BOUND, - Float.toString(mShortTermModelUpperLuxMultiplier)); + serializer.attributeFloat(null, ATTR_MODEL_UPPER_BOUND, + mShortTermModelUpperLuxMultiplier); } serializer.endTag(null, TAG_BRIGHTNESS_PARAMS); } @@ -408,7 +407,7 @@ public final class BrightnessConfiguration implements Parcelable { * * @hide */ - public static BrightnessConfiguration loadFromXml(@NonNull XmlPullParser parser) + public static BrightnessConfiguration loadFromXml(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException { String description = null; List<Float> luxList = new ArrayList<>(); @@ -440,22 +439,17 @@ public final class BrightnessConfiguration implements Parcelable { continue; } final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); - final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY); + final int category = parser.getAttributeInt(null, ATTR_CATEGORY, -1); BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser); if (packageName != null) { correctionsByPackageName.put(packageName, correction); - } else if (categoryText != null) { - try { - final int category = Integer.parseInt(categoryText); - correctionsByCategory.put(category, correction); - } catch (NullPointerException | NumberFormatException e) { - continue; - } + } else if (category != -1) { + correctionsByCategory.put(category, correction); } } } else if (TAG_BRIGHTNESS_PARAMS.equals(parser.getName())) { shouldCollectColorSamples = - Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_COLLECT_COLOR)); + parser.getAttributeBoolean(null, ATTR_COLLECT_COLOR, false); Long timeout = loadLongFromXml(parser, ATTR_MODEL_TIMEOUT); if (timeout != null) { shortTermModelTimeout = timeout; @@ -491,23 +485,16 @@ public final class BrightnessConfiguration implements Parcelable { return builder.build(); } - private static float loadFloatFromXml(XmlPullParser parser, String attribute) { - final String string = parser.getAttributeValue(null, attribute); - try { - return Float.parseFloat(string); - } catch (NullPointerException | NumberFormatException e) { - return Float.NaN; - } + private static float loadFloatFromXml(TypedXmlPullParser parser, String attribute) { + return parser.getAttributeFloat(null, attribute, Float.NaN); } - private static Long loadLongFromXml(XmlPullParser parser, String attribute) { - final String string = parser.getAttributeValue(null, attribute); + private static Long loadLongFromXml(TypedXmlPullParser parser, String attribute) { try { - return Long.parseLong(string); - } catch (NullPointerException | NumberFormatException e) { - // Ignoring + return parser.getAttributeLong(null, attribute); + } catch (Exception e) { + return null; } - return null; } /** diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java index bbfc45edb89a..2919ec3fbf08 100644 --- a/core/java/android/hardware/display/BrightnessCorrection.java +++ b/core/java/android/hardware/display/BrightnessCorrection.java @@ -23,12 +23,12 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.util.MathUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; @@ -153,7 +153,7 @@ public final class BrightnessCorrection implements Parcelable { * * @hide */ - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { mImplementation.saveToXml(serializer); } @@ -170,7 +170,7 @@ public final class BrightnessCorrection implements Parcelable { * * @hide */ - public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, + public static BrightnessCorrection loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { @@ -181,20 +181,15 @@ public final class BrightnessCorrection implements Parcelable { return null; } - private static float loadFloatFromXml(XmlPullParser parser, String attribute) { - final String string = parser.getAttributeValue(null, attribute); - try { - return Float.parseFloat(string); - } catch (NullPointerException | NumberFormatException e) { - return Float.NaN; - } + private static float loadFloatFromXml(TypedXmlPullParser parser, String attribute) { + return parser.getAttributeFloat(null, attribute, Float.NaN); } private interface BrightnessCorrectionImplementation { float apply(float brightness); String toString(); void writeToParcel(Parcel dest); - void saveToXml(XmlSerializer serializer) throws IOException; + void saveToXml(TypedXmlSerializer serializer) throws IOException; // Package-private static methods: // static BrightnessCorrection readFromParcel(Parcel in); // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, @@ -263,10 +258,10 @@ public final class BrightnessCorrection implements Parcelable { } @Override - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG); - serializer.attribute(null, ATTR_SCALE, Float.toString(mScale)); - serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate)); + serializer.attributeFloat(null, ATTR_SCALE, mScale); + serializer.attributeFloat(null, ATTR_TRANSLATE, mTranslate); serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG); } @@ -276,7 +271,7 @@ public final class BrightnessCorrection implements Parcelable { return BrightnessCorrection.createScaleAndTranslateLog(scale, translate); } - static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, + static BrightnessCorrection loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final float scale = loadFloatFromXml(parser, ATTR_SCALE); final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE); diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index ca5eeb1863c7..9bae1ff4b906 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -19,6 +19,7 @@ package android.hardware.display; import static android.view.Display.DEFAULT_DISPLAY; import android.Manifest; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -38,9 +39,12 @@ import android.util.SparseArray; import android.view.Display; import android.view.Surface; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; + /** * Manages the properties of attached displays. */ @@ -336,6 +340,40 @@ public final class DisplayManager { */ public static final int VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP = 1 << 11; + + /** @hide */ + @IntDef(prefix = {"SWITCHING_TYPE_"}, value = { + SWITCHING_TYPE_NONE, + SWITCHING_TYPE_WITHIN_GROUPS, + SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SwitchingType {} + + /** + * No mode switching will happen. + * @hide + */ + @TestApi + public static final int SWITCHING_TYPE_NONE = 0; + + /** + * Allow only refresh rate switching between modes in the same configuration group. This way + * only switches without visual interruptions for the user will be allowed. + * @hide + */ + @TestApi + public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; + + /** + * Allow refresh rate switching between all refresh rates even if the switch with have visual + * interruptions for the user. + * @hide + */ + @TestApi + public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; + + /** @hide */ public DisplayManager(Context context) { mContext = context; @@ -875,6 +913,29 @@ public final class DisplayManager { } /** + * Sets the refresh rate switching type. + * This matches {@link android.provider.Settings.Secure.MATCH_CONTENT_FRAME_RATE} + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) + public void setRefreshRateSwitchingType(@SwitchingType int newValue) { + mGlobal.setRefreshRateSwitchingType(newValue); + } + + /** + * Returns the refresh rate switching type. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) + @SwitchingType public int getRefreshRateSwitchingType() { + return mGlobal.getRefreshRateSwitchingType(); + } + + /** * Listens for changes in available display devices. */ public interface DisplayListener { diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 7b4889f0a1b3..77ae9471dfb0 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -725,6 +725,33 @@ public final class DisplayManagerGlobal { } } + /** + * Sets the refresh rate switching type. + * + * @hide + */ + public void setRefreshRateSwitchingType(@DisplayManager.SwitchingType int newValue) { + try { + mDm.setRefreshRateSwitchingType(newValue); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Returns the refresh rate switching type. + * + * @hide + */ + @DisplayManager.SwitchingType + public int getRefreshRateSwitchingType() { + try { + return mDm.getRefreshRateSwitchingType(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { @Override public void onDisplayEvent(int displayId, int event) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 85da6424377a..a9f78fa03a6d 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -134,4 +134,10 @@ interface IDisplayManager { // battery etc. void setShouldAlwaysRespectAppRequestedMode(boolean enabled); boolean shouldAlwaysRespectAppRequestedMode(); + + // Sets the refresh rate switching type. + void setRefreshRateSwitchingType(int newValue); + + // Returns the refresh rate switching type. + int getRefreshRateSwitchingType(); } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index c5c51e4661c5..468157a19971 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -33,8 +33,8 @@ interface IFaceService { // Creates a test session with the specified sensorId ITestSession createTestSession(int sensorId, String opPackageName); - // Requests a proto dump of the service to the specified fd - byte[] dumpSensorServiceStateProto(); + // Requests a proto dump of the specified sensor + byte[] dumpSensorServiceStateProto(int sensorId); // Retrieve static sensor properties for all face sensors List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName); diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index a4ce29ba4ea0..64abbea12de0 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -34,8 +34,8 @@ interface IFingerprintService { // Creates a test session with the specified sensorId ITestSession createTestSession(int sensorId, String opPackageName); - // Requests a proto dump of the service to the specified fd - byte[] dumpSensorServiceStateProto(); + // Requests a proto dump of the specified sensor + byte[] dumpSensorServiceStateProto(int sensorId); // Retrieve static sensor properties for all fingerprint sensors List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName); diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index eef4089ac336..ae10f4006ce5 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -367,35 +367,35 @@ public final class HdmiControlManager { @Retention(RetentionPolicy.SOURCE) public @interface HdmiCecVersion {} - // -- Which devices the playback device can send a <Standby> message to upon going to sleep. + // -- Scope of CEC power control messages sent by a playback device. /** - * Send <Standby> to TV only. + * Send CEC power control messages to TV only. * * @hide */ - public static final String SEND_STANDBY_ON_SLEEP_TO_TV = "to_tv"; + public static final String POWER_CONTROL_MODE_TV = "to_tv"; /** - * Broadcast <Standby> to all devices in the network. + * Broadcast CEC power control messages to all devices in the network. * * @hide */ - public static final String SEND_STANDBY_ON_SLEEP_BROADCAST = "broadcast"; + public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast"; /** - * Don't send any <Standby> message. + * Don't send any CEC power control messages. * * @hide */ - public static final String SEND_STANDBY_ON_SLEEP_NONE = "none"; + public static final String POWER_CONTROL_MODE_NONE = "none"; /** * @hide */ @StringDef({ - SEND_STANDBY_ON_SLEEP_TO_TV, - SEND_STANDBY_ON_SLEEP_BROADCAST, - SEND_STANDBY_ON_SLEEP_NONE + POWER_CONTROL_MODE_TV, + POWER_CONTROL_MODE_BROADCAST, + POWER_CONTROL_MODE_NONE }) @Retention(RetentionPolicy.SOURCE) - public @interface StandbyBehavior {} + public @interface PowerControlMode {} // -- Which power state action should be taken when Active Source is lost. /** @@ -457,11 +457,11 @@ public final class HdmiControlManager { */ public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version"; /** - * Name of a setting deciding on the Standby message behaviour on sleep. + * Name of a setting deciding on the power control mode. * * @hide */ - public static final String CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP = "send_standby_on_sleep"; + public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "send_standby_on_sleep"; /** * Name of a setting deciding on power state action when losing Active Source. * @@ -482,7 +482,7 @@ public final class HdmiControlManager { @StringDef({ CEC_SETTING_NAME_HDMI_CEC_ENABLED, CEC_SETTING_NAME_HDMI_CEC_VERSION, - CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, + CEC_SETTING_NAME_POWER_CONTROL_MODE, CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, }) @@ -1506,7 +1506,7 @@ public final class HdmiControlManager { } /** - * Set the 'send_standby_on_sleep' option. + * Set the 'power_control_mode' option. * * @param value the desired value * @throws IllegalArgumentException when the new value is not allowed. @@ -1515,20 +1515,20 @@ public final class HdmiControlManager { * @hide */ @RequiresPermission(android.Manifest.permission.HDMI_CEC) - public void setSendStandbyOnSleep(@NonNull @StandbyBehavior String value) { + public void setPowerControlMode(@NonNull @PowerControlMode String value) { if (mService == null) { Log.e(TAG, "HdmiControlService is not available"); throw new RuntimeException("HdmiControlService is not available"); } try { - mService.setCecSettingStringValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, value); + mService.setCecSettingStringValue(CEC_SETTING_NAME_POWER_CONTROL_MODE, value); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Get the value of 'send_standby_on_sleep' option. + * Get the value of 'power_control_mode' option. * * @return the current value. * @throws RuntimeException when the HdmiControlService is not available. @@ -1536,15 +1536,15 @@ public final class HdmiControlManager { * @hide */ @NonNull - @StandbyBehavior + @PowerControlMode @RequiresPermission(android.Manifest.permission.HDMI_CEC) - public String getSendStandbyOnSleep() { + public String getPowerControlMode() { if (mService == null) { Log.e(TAG, "HdmiControlService is not available"); throw new RuntimeException("HdmiControlService is not available"); } try { - return mService.getCecSettingStringValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); + return mService.getCecSettingStringValue(CEC_SETTING_NAME_POWER_CONTROL_MODE); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index aac57fe88b17..c28bab7f643f 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -609,7 +609,7 @@ public class UsbManager { public @interface UsbFunctionMode {} /** @hide */ - @IntDef(flag = true, prefix = { "GADGET_HAL_" }, value = { + @IntDef(prefix = { "GADGET_HAL_" }, value = { GADGET_HAL_NOT_SUPPORTED, GADGET_HAL_V1_0, GADGET_HAL_V1_1, diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java index 43c418e2cb26..bb7aff651b3d 100644 --- a/core/java/android/hardware/usb/UsbPortStatus.java +++ b/core/java/android/hardware/usb/UsbPortStatus.java @@ -202,7 +202,7 @@ public final class UsbPortStatus implements Parcelable { public static final int CONTAMINANT_PROTECTION_DISABLED = android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.DISABLED; - @IntDef(prefix = { "CONTAMINANT_DETECION_" }, flag = true, value = { + @IntDef(prefix = { "CONTAMINANT_DETECTION_" }, value = { CONTAMINANT_DETECTION_NOT_SUPPORTED, CONTAMINANT_DETECTION_DISABLED, CONTAMINANT_DETECTION_NOT_DETECTED, @@ -221,7 +221,7 @@ public final class UsbPortStatus implements Parcelable { @Retention(RetentionPolicy.SOURCE) @interface ContaminantProtectionStatus{} - @IntDef(prefix = { "MODE_" }, flag = true, value = { + @IntDef(prefix = { "MODE_" }, value = { MODE_NONE, MODE_DFP, MODE_UFP, diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 5576857d1f6b..6831eca32f72 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -630,6 +630,9 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public void updateInputMethodDisplay(int displayId) { + if (getDisplayId() == displayId) { + return; + } // Update display for adding IME window to the right display. // TODO(b/111364446) Need to address context lifecycle issue if need to re-create // for update resources & configuration correctly when show soft input @@ -804,12 +807,12 @@ public class InputMethodService extends AbstractInputMethodService { null /* icProto */); final boolean wasVisible = isInputViewShown(); if (dispatchOnShowInputRequested(flags, false)) { - showWindow(true); applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */); + } else { + // If user uses hard keyboard, IME button should always be shown. + setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); } - // If user uses hard keyboard, IME button should always be shown. - setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); final boolean isVisible = isInputViewShown(); final boolean visibilityChanged = isVisible != wasVisible; if (resultReceiver != null) { @@ -1273,6 +1276,9 @@ public class InputMethodService extends AbstractInputMethodService { super.onCreate(); mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); mSettingsObserver = SettingsObserver.createAndRegister(this); + // cache preference so we don't have to read ContentProvider when IME is requested to be + // shown the first time (cold start). + mSettingsObserver.shouldShowImeWithHardKeyboard(); mIsAutomotive = isAutomotive(); mAutomotiveHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean( @@ -1341,13 +1347,7 @@ public class InputMethodService extends AbstractInputMethodService { mRootView = mInflater.inflate( com.android.internal.R.layout.input_method, null); mWindow.setContentView(mRootView); - mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer); mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); - if (Settings.Global.getInt(getContentResolver(), - Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) { - mWindow.getWindow().setWindowAnimations( - com.android.internal.R.style.Animation_InputMethodFancy); - } mFullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea); mExtractViewHidden = false; mExtractFrame = mRootView.findViewById(android.R.id.extractArea); @@ -1413,6 +1413,7 @@ public class InputMethodService extends AbstractInputMethodService { int showFlags = mShowInputFlags; boolean showingInput = mShowInputRequested; CompletionInfo[] completions = mCurCompletions; + mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer); initViews(); mInputViewStarted = false; mCandidatesViewStarted = false; diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java index 522add12827e..8b06ebe105d0 100644 --- a/core/java/android/net/ConnectivityMetricsEvent.java +++ b/core/java/android/net/ConnectivityMetricsEvent.java @@ -19,7 +19,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; -import com.android.internal.util.BitUtils; +import java.util.BitSet; /** * Represents a core networking event defined in package android.net.metrics. @@ -86,9 +86,7 @@ public final class ConnectivityMetricsEvent implements Parcelable { if (ifname != null) { buffer.append(", ").append(ifname); } - for (int t : BitUtils.unpackBits(transports)) { - buffer.append(", ").append(NetworkCapabilities.transportNameOf(t)); - } + buffer.append(", transports=").append(BitSet.valueOf(new long[] { transports })); buffer.append("): ").append(data.toString()); return buffer.toString(); } diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java index 9876fc650f21..183f500572bd 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -16,12 +16,9 @@ package android.net; -import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK; -import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA; -import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; - import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.internal.util.Preconditions.checkStringNotEmpty; +import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU; import android.annotation.NonNull; import android.annotation.Nullable; @@ -158,9 +155,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6 // networks, the VPN must provide a link fulfilling the stricter of the two conditions // (at least that of the IPv6 MTU). - if (mMaxMtu < LinkProperties.MIN_MTU_V6) { - throw new IllegalArgumentException( - "Max MTU must be at least" + LinkProperties.MIN_MTU_V6); + if (mMaxMtu < IPV6_MIN_MTU) { + throw new IllegalArgumentException("Max MTU must be at least" + IPV6_MIN_MTU); } switch (mType) { @@ -811,9 +807,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6 // networks, the VPN must provide a link fulfilling the stricter of the two conditions // (at least that of the IPv6 MTU). - if (mtu < LinkProperties.MIN_MTU_V6) { - throw new IllegalArgumentException( - "Max MTU must be at least " + LinkProperties.MIN_MTU_V6); + if (mtu < IPV6_MIN_MTU) { + throw new IllegalArgumentException("Max MTU must be at least " + IPV6_MIN_MTU); } mMaxMtu = mtu; return this; diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index fa1497dcbc43..b48c1fdaf1b2 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -17,8 +17,6 @@ package android.net; import static android.net.IpSecManager.INVALID_RESOURCE_ID; -import static com.android.internal.util.Preconditions.checkNotNull; - import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -28,7 +26,6 @@ import android.annotation.SystemApi; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -182,7 +179,6 @@ public final class IpSecTransform implements AutoCloseable { try { IIpSecService svc = getIpSecService(); svc.deleteTransform(mResourceId); - stopNattKeepalive(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } catch (Exception e) { @@ -213,36 +209,6 @@ public final class IpSecTransform implements AutoCloseable { private int mResourceId; private final Context mContext; private final CloseGuard mCloseGuard = CloseGuard.get(); - private ConnectivityManager.PacketKeepalive mKeepalive; - private Handler mCallbackHandler; - private final ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback = - new ConnectivityManager.PacketKeepaliveCallback() { - - @Override - public void onStarted() { - synchronized (this) { - mCallbackHandler.post(() -> mUserKeepaliveCallback.onStarted()); - } - } - - @Override - public void onStopped() { - synchronized (this) { - mKeepalive = null; - mCallbackHandler.post(() -> mUserKeepaliveCallback.onStopped()); - } - } - - @Override - public void onError(int error) { - synchronized (this) { - mKeepalive = null; - mCallbackHandler.post(() -> mUserKeepaliveCallback.onError(error)); - } - } - }; - - private NattKeepaliveCallback mUserKeepaliveCallback; /** @hide */ @VisibleForTesting @@ -274,76 +240,6 @@ public final class IpSecTransform implements AutoCloseable { public void onError(int error) {} } - /** - * Start a NAT-T keepalive session for the current transform. - * - * For a transform that is using UDP encapsulated IPv4, NAT-T offloading provides - * a power efficient mechanism of sending NAT-T packets at a specified interval. - * - * @param userCallback a {@link #NattKeepaliveCallback} to receive asynchronous status - * information about the requested NAT-T keepalive session. - * @param intervalSeconds the interval between NAT-T keepalives being sent. The - * the allowed range is between 20 and 3600 seconds. - * @param handler a handler on which to post callbacks when received. - * - * @hide - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.MANAGE_IPSEC_TUNNELS, - android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD - }) - public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback, - int intervalSeconds, @NonNull Handler handler) throws IOException { - checkNotNull(userCallback); - if (intervalSeconds < 20 || intervalSeconds > 3600) { - throw new IllegalArgumentException("Invalid NAT-T keepalive interval"); - } - checkNotNull(handler); - if (mResourceId == INVALID_RESOURCE_ID) { - throw new IllegalStateException( - "Packet keepalive cannot be started for an inactive transform"); - } - - synchronized (mKeepaliveCallback) { - if (mKeepaliveCallback != null) { - throw new IllegalStateException("Keepalive already active"); - } - - mUserKeepaliveCallback = userCallback; - ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - mKeepalive = cm.startNattKeepalive( - mConfig.getNetwork(), intervalSeconds, mKeepaliveCallback, - NetworkUtils.numericToInetAddress(mConfig.getSourceAddress()), - 4500, // FIXME urgently, we need to get the port number from the Encap socket - NetworkUtils.numericToInetAddress(mConfig.getDestinationAddress())); - mCallbackHandler = handler; - } - } - - /** - * Stop an ongoing NAT-T keepalive session. - * - * Calling this API will request that an ongoing NAT-T keepalive session be terminated. - * If this API is not called when a Transform is closed, the underlying NAT-T session will - * be terminated automatically. - * - * @hide - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.MANAGE_IPSEC_TUNNELS, - android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD - }) - public void stopNattKeepalive() { - synchronized (mKeepaliveCallback) { - if (mKeepalive == null) { - Log.e(TAG, "No active keepalive to stop"); - return; - } - mKeepalive.stop(); - } - } - /** This class is used to build {@link IpSecTransform} objects. */ public static class Builder { private Context mContext; diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 81e6e788734b..e41ed72b259c 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -82,8 +82,7 @@ public final class LinkProperties implements Parcelable { private static final int MIN_MTU = 68; - /** @hide */ - public static final int MIN_MTU_V6 = 1280; + private static final int MIN_MTU_V6 = 1280; private static final int MAX_MTU = 10000; diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index 9c1038846d70..e65c27c2f4bb 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -17,8 +17,6 @@ package android.net; import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.ConnectivityManager.getNetworkTypeName; -import static android.net.ConnectivityManager.isNetworkTypeMobile; import android.annotation.Nullable; import android.content.Context; @@ -27,7 +25,6 @@ import android.net.wifi.WifiManager; import android.os.Build; import android.service.NetworkIdentityProto; import android.telephony.Annotation.NetworkType; -import android.util.Slog; import android.util.proto.ProtoOutputStream; import java.util.Objects; @@ -85,7 +82,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { @Override public String toString() { final StringBuilder builder = new StringBuilder("{"); - builder.append("type=").append(getNetworkTypeName(mType)); + builder.append("type=").append(mType); builder.append(", subType="); if (mSubType == SUBTYPE_COMBINED) { builder.append("COMBINED"); @@ -195,18 +192,9 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { boolean metered = !state.networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - if (isNetworkTypeMobile(type)) { - if (state.subscriberId == null) { - if (state.networkInfo.getState() != NetworkInfo.State.DISCONNECTED && - state.networkInfo.getState() != NetworkInfo.State.UNKNOWN) { - Slog.w(TAG, "Active mobile network without subscriber! ni = " - + state.networkInfo); - } - } - - subscriberId = state.subscriberId; + subscriberId = state.subscriberId; - } else if (type == TYPE_WIFI) { + if (type == TYPE_WIFI) { if (state.networkId != null) { networkId = state.networkId; } else { diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index a643d09eef49..f05f033b2fa5 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -144,6 +144,8 @@ public class NetworkPolicyManager { public static final String FIREWALL_CHAIN_NAME_STANDBY = "standby"; /** @hide */ public static final String FIREWALL_CHAIN_NAME_POWERSAVE = "powersave"; + /** @hide */ + public static final String FIREWALL_CHAIN_NAME_RESTRICTED = "restricted"; private static final boolean ALLOW_PLATFORM_APP_POLICY = true; diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java index a17a49897d39..14cb51c85d06 100644 --- a/core/java/android/net/NetworkProvider.java +++ b/core/java/android/net/NetworkProvider.java @@ -63,7 +63,7 @@ public class NetworkProvider { private final Messenger mMessenger; private final String mName; - private final ConnectivityManager mCm; + private final Context mContext; private int mProviderId = ID_NONE; @@ -78,8 +78,6 @@ public class NetworkProvider { */ @SystemApi public NetworkProvider(@NonNull Context context, @NonNull Looper looper, @NonNull String name) { - mCm = ConnectivityManager.from(context); - Handler handler = new Handler(looper) { @Override public void handleMessage(Message m) { @@ -95,6 +93,7 @@ public class NetworkProvider { } } }; + mContext = context; mMessenger = new Messenger(handler); mName = name; } @@ -158,6 +157,6 @@ public class NetworkProvider { @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) { - mCm.declareNetworkRequestUnfulfillable(request); + ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(request); } } diff --git a/core/java/android/net/metrics/ConnectStats.java b/core/java/android/net/metrics/ConnectStats.java index b320b755f3f4..c67259d6b29b 100644 --- a/core/java/android/net/metrics/ConnectStats.java +++ b/core/java/android/net/metrics/ConnectStats.java @@ -16,14 +16,14 @@ package android.net.metrics; -import android.net.NetworkCapabilities; import android.system.OsConstants; import android.util.IntArray; import android.util.SparseIntArray; -import com.android.internal.util.BitUtils; import com.android.internal.util.TokenBucket; +import java.util.BitSet; + /** * A class that aggregates connect() statistics. * {@hide} @@ -120,10 +120,9 @@ public class ConnectStats { @Override public String toString() { StringBuilder builder = - new StringBuilder("ConnectStats(").append("netId=").append(netId).append(", "); - for (int t : BitUtils.unpackBits(transports)) { - builder.append(NetworkCapabilities.transportNameOf(t)).append(", "); - } + new StringBuilder("ConnectStats(").append("netId=").append(netId) + .append(", transports=").append(BitSet.valueOf(new long[] { transports })) + .append(", "); builder.append(String.format("%d events, ", eventCount)); builder.append(String.format("%d success, ", connectCount)); builder.append(String.format("%d blocking, ", connectBlockingCount)); diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java index 6f383b4d515b..8988983d175c 100644 --- a/core/java/android/net/metrics/DefaultNetworkEvent.java +++ b/core/java/android/net/metrics/DefaultNetworkEvent.java @@ -16,12 +16,7 @@ package android.net.metrics; -import static android.net.ConnectivityManager.NETID_UNSET; - -import android.net.NetworkCapabilities; - -import com.android.internal.util.BitUtils; - +import java.util.BitSet; import java.util.StringJoiner; /** @@ -32,8 +27,8 @@ public class DefaultNetworkEvent { // The creation time in milliseconds of this DefaultNetworkEvent. public final long creationTimeMs; - // The network ID of the network or NETID_UNSET if none. - public int netId = NETID_UNSET; + // The network ID of the network or 0 if none. + public int netId = 0; // The list of transport types, as defined in NetworkCapabilities.java. public int transports; // The list of transport types of the last previous default network. @@ -63,9 +58,7 @@ public class DefaultNetworkEvent { public String toString() { StringJoiner j = new StringJoiner(", ", "DefaultNetworkEvent(", ")"); j.add("netId=" + netId); - for (int t : BitUtils.unpackBits(transports)) { - j.add(NetworkCapabilities.transportNameOf(t)); - } + j.add("transports=" + BitSet.valueOf(new long[] { transports })); j.add("ip=" + ipSupport()); if (initialScore > 0) { j.add("initial_score=" + initialScore); diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java index 5aa705b0edf3..bf351ce07fe8 100644 --- a/core/java/android/net/metrics/DnsEvent.java +++ b/core/java/android/net/metrics/DnsEvent.java @@ -16,11 +16,8 @@ package android.net.metrics; -import android.net.NetworkCapabilities; - -import com.android.internal.util.BitUtils; - import java.util.Arrays; +import java.util.BitSet; /** * A batch of DNS events recorded by NetdEventListenerService for a specific network. @@ -86,10 +83,10 @@ final public class DnsEvent { @Override public String toString() { StringBuilder builder = - new StringBuilder("DnsEvent(").append("netId=").append(netId).append(", "); - for (int t : BitUtils.unpackBits(transports)) { - builder.append(NetworkCapabilities.transportNameOf(t)).append(", "); - } + new StringBuilder("DnsEvent(").append("netId=").append(netId) + .append(", transports=") + .append(BitSet.valueOf(new long[] { transports })) + .append(", "); builder.append(String.format("%d events, ", eventCount)); builder.append(String.format("%d success)", successCount)); return builder.toString(); diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java index 66d92c48087c..8f2f612cf53b 100644 --- a/core/java/android/net/metrics/NetworkMetrics.java +++ b/core/java/android/net/metrics/NetworkMetrics.java @@ -16,11 +16,9 @@ package android.net.metrics; -import android.net.NetworkCapabilities; - -import com.android.internal.util.BitUtils; import com.android.internal.util.TokenBucket; +import java.util.BitSet; import java.util.StringJoiner; /** @@ -144,9 +142,7 @@ public class NetworkMetrics { public String toString() { StringJoiner j = new StringJoiner(", ", "{", "}"); j.add("netId=" + netId); - for (int t : BitUtils.unpackBits(transports)) { - j.add(NetworkCapabilities.transportNameOf(t)); - } + j.add("transports=" + BitSet.valueOf(new long[] { transports })); j.add(String.format("dns avg=%dms max=%dms err=%.1f%% tot=%d", (int) dnsLatencies.average(), (int) dnsLatencies.max, 100 * dnsErrorRate.average(), dnsErrorRate.count)); diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java new file mode 100644 index 000000000000..d00c3c361722 --- /dev/null +++ b/core/java/android/os/BatteryConsumer.java @@ -0,0 +1,89 @@ +/* + * 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.os; + +import android.annotation.IntDef; +import android.annotation.NonNull; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Interface for objects containing battery attribution data. + * + * @hide + */ +public abstract class BatteryConsumer { + + /** + * Power usage component, describing the particular part of the system + * responsible for power drain. + * + * @hide + */ + @IntDef(prefix = {"POWER_COMPONENT_"}, value = { + POWER_COMPONENT_CPU, + }) + @Retention(RetentionPolicy.SOURCE) + public static @interface PowerComponent { + } + + public static final int POWER_COMPONENT_CPU = 0; + + public static final int POWER_COMPONENT_COUNT = 1; + + public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000; + public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999; + + private final PowerComponents mPowerComponents; + + protected BatteryConsumer(@NonNull PowerComponents powerComponents) { + mPowerComponents = powerComponents; + } + + /** + * Total power consumed by this consumer, in mAh. + */ + public double getConsumedPower() { + return mPowerComponents.getTotalPowerConsumed(); + } + + /** + * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. + * + * @param componentId The ID of the power component, e.g. + * {@link BatteryConsumer#POWER_COMPONENT_CPU}. + * @return Amount of consumed power in mAh. + */ + public double getConsumedPower(@PowerComponent int componentId) { + return mPowerComponents.getConsumedPower(componentId); + } + + /** + * Returns the amount of drain attributed to the specified custom drain type. + * + * @param componentId The ID of the custom power component. + * @return Amount of consumed power in mAh. + */ + public double getConsumedPowerForCustomComponent(int componentId) { + return mPowerComponents.getConsumedPowerForCustomComponent(componentId); + } + + protected void writeToParcel(Parcel dest, int flags) { + mPowerComponents.writeToParcel(dest, flags); + } +} diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java index a9585c62866b..d9e01cd4fc7d 100644 --- a/core/java/android/os/BatteryStatsManager.java +++ b/core/java/android/os/BatteryStatsManager.java @@ -161,6 +161,22 @@ public final class BatteryStatsManager { } /** + * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem + * and per-UID basis. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.BATTERY_STATS) + @NonNull + public BatteryUsageStats getBatteryUsageStats() { + try { + return mBatteryStats.getBatteryUsageStats(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Indicates that the wifi connection RSSI has changed. * * @param newRssi The new RSSI value. diff --git a/cmds/statsd/benchmark/main.cpp b/core/java/android/os/BatteryUsageStats.aidl index 08921f3c3f52..0400f19974f1 100644 --- a/cmds/statsd/benchmark/main.cpp +++ b/core/java/android/os/BatteryUsageStats.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,6 @@ * limitations under the License. */ -#include <benchmark/benchmark.h> +package android.os; -BENCHMARK_MAIN(); +parcelable BatteryUsageStats; diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java new file mode 100644 index 000000000000..3f036cdcfa72 --- /dev/null +++ b/core/java/android/os/BatteryUsageStats.java @@ -0,0 +1,138 @@ +/* + * 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.os; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; + +import java.util.ArrayList; +import java.util.List; + +/** + * Contains a snapshot of battery attribution data, on a per-subsystem and per-UID basis. + * + * @hide + */ +public final class BatteryUsageStats implements Parcelable { + private final double mConsumedPower; + private final int mDischargePercentage; + private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers; + + private BatteryUsageStats(@NonNull Builder builder) { + mConsumedPower = builder.mConsumedPower; + mDischargePercentage = builder.mDischargePercentage; + mUidBatteryConsumers = builder.mUidBatteryConsumers; + } + + /** + * Portion of battery charge drained since BatteryStats reset (e.g. due to being fully + * charged), as percentage of the full charge in the range [0:100] + */ + public int getDischargePercentage() { + return mDischargePercentage; + } + + /** + * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully + * charged), in mAh + */ + public double getConsumedPower() { + return mConsumedPower; + } + + @NonNull + public List<UidBatteryConsumer> getUidBatteryConsumers() { + return mUidBatteryConsumers; + } + + @Override + public int describeContents() { + return 0; + } + + private BatteryUsageStats(@NonNull Parcel source) { + mUidBatteryConsumers = new ArrayList<>(); + source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader()); + mConsumedPower = source.readDouble(); + mDischargePercentage = source.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelableList(mUidBatteryConsumers, flags); + dest.writeDouble(mConsumedPower); + dest.writeInt(mDischargePercentage); + } + + @NonNull + public static final Creator<BatteryUsageStats> CREATOR = new Creator<BatteryUsageStats>() { + public BatteryUsageStats createFromParcel(@NonNull Parcel source) { + return new BatteryUsageStats(source); + } + + public BatteryUsageStats[] newArray(int size) { + return new BatteryUsageStats[size]; + } + }; + + /** + * Builder for BatteryUsageStats. + */ + public static final class Builder { + private double mConsumedPower; + private int mDischargePercentage; + private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers = new ArrayList<>(); + + /** + * Constructs a read-only object using the Builder values. + */ + @NonNull + public BatteryUsageStats build() { + return new BatteryUsageStats(this); + } + + /** + * Sets the battery discharge amount since BatteryStats reset as percentage of the full + * charge. + */ + @SuppressLint("PercentageInt") // See b/174188159 + @NonNull + public Builder setDischargePercentage(int dischargePercentage) { + mDischargePercentage = dischargePercentage; + return this; + } + + /** + * Sets the battery discharge amount since BatteryStats reset, in mAh. + */ + @NonNull + public Builder setConsumedPower(double consumedPower) { + mConsumedPower = consumedPower; + return this; + } + + /** + * Adds a UidBatteryConsumer, which represents battery attribution data for an + * individual UID. + */ + @NonNull + public Builder addUidBatteryConsumer(@NonNull UidBatteryConsumer uidBatteryConsumer) { + mUidBatteryConsumers.add(uidBatteryConsumer); + return this; + } + } +} diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index a4af0dbed0bd..e2e1bbe8487f 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1946,7 +1946,13 @@ public final class Debug */ public static final int MEMINFO_KRECLAIMABLE = 15; /** @hide */ - public static final int MEMINFO_COUNT = 16; + public static final int MEMINFO_ACTIVE = 16; + /** @hide */ + public static final int MEMINFO_INACTIVE = 17; + /** @hide */ + public static final int MEMINFO_UNEVICTABLE = 18; + /** @hide */ + public static final int MEMINFO_COUNT = 19; /** * Retrieves /proc/meminfo. outSizes is filled with fields diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 5db4107f02f1..379d6e6f5dfe 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -1436,7 +1436,8 @@ public final class FileUtils { public static FileDescriptor convertToModernFd(FileDescriptor fd) { try { Context context = AppGlobals.getInitialApplication(); - if (!SystemProperties.getBoolean("persist.sys.fuse.transcode", false) + // TODO(b/169327180): Consider device config. + if (!SystemProperties.getBoolean("persist.sys.fuse.transcode_enabled", false) || !SystemProperties.getBoolean("persist.sys.fuse.transcode_optimize", true) || UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) { // If transcode is enabled we optimize by default, unless explicitly disabled. diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java new file mode 100644 index 000000000000..42ba1ff60e5a --- /dev/null +++ b/core/java/android/os/PowerComponents.java @@ -0,0 +1,170 @@ +/* + * 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.os; + + +import android.annotation.NonNull; + +/** + * Contains details of battery attribution data broken down to individual power drain types + * such as CPU, RAM, GPU etc. + * + * @hide + */ +class PowerComponents { + + private final double mTotalPowerConsumed; + private final double[] mPowerComponents; + + PowerComponents(@NonNull Builder builder) { + mTotalPowerConsumed = builder.mTotalPowerConsumed; + mPowerComponents = builder.mPowerComponents; + } + + PowerComponents(@NonNull Parcel source) { + mTotalPowerConsumed = source.readDouble(); + mPowerComponents = source.createDoubleArray(); + } + + /** Writes contents to Parcel */ + void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeDouble(mTotalPowerConsumed); + dest.writeDoubleArray(mPowerComponents); + } + + /** + * Total power consumed by this consumer, in mAh. + */ + public double getTotalPowerConsumed() { + return mTotalPowerConsumed; + } + + /** + * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. + * + * @param componentId The ID of the power component, e.g. + * {@link BatteryConsumer#POWER_COMPONENT_CPU}. + * @return Amount of consumed power in mAh. + */ + public double getConsumedPower(@UidBatteryConsumer.PowerComponent int componentId) { + if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) { + throw new IllegalArgumentException( + "Unsupported power component ID: " + componentId); + } + try { + return mPowerComponents[componentId]; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException("Unsupported power component ID: " + componentId); + } + } + + /** + * Returns the amount of drain attributed to the specified custom drain type. + * + * @param componentId The ID of the custom power component. + * @return Amount of consumed power in mAh. + */ + public double getConsumedPowerForCustomComponent(int componentId) { + if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) { + throw new IllegalArgumentException( + "Unsupported custom power component ID: " + componentId); + } + try { + return mPowerComponents[ + BatteryConsumer.POWER_COMPONENT_COUNT + componentId + - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID]; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException( + "Unsupported custom power component ID: " + componentId); + } + } + + /** + * Builder for PowerComponents. + */ + static final class Builder { + private double mTotalPowerConsumed; + private final double[] mPowerComponents; + + Builder(int customPowerComponentCount) { + mPowerComponents = new double[BatteryConsumer.POWER_COMPONENT_COUNT + + customPowerComponentCount]; + } + + /** + * Sets the sum amount of power consumed since BatteryStats reset. + */ + @NonNull + public Builder setTotalPowerConsumed(double totalPowerConsumed) { + mTotalPowerConsumed = totalPowerConsumed; + return this; + } + + /** + * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. + * + * @param componentId The ID of the power component, e.g. + * {@link BatteryConsumer#POWER_COMPONENT_CPU}. + * @param componentPower Amount of consumed power in mAh. + */ + @NonNull + public Builder setConsumedPower(@UidBatteryConsumer.PowerComponent int componentId, + double componentPower) { + if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) { + throw new IllegalArgumentException( + "Unsupported power component ID: " + componentId); + } + try { + mPowerComponents[componentId] = componentPower; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException( + "Unsupported power component ID: " + componentId); + } + return this; + } + + /** + * Sets the amount of drain attributed to the specified custom drain type. + * + * @param componentId The ID of the custom power component. + * @param componentPower Amount of consumed power in mAh. + */ + @NonNull + public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) { + if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) { + throw new IllegalArgumentException( + "Unsupported custom power component ID: " + componentId); + } + try { + mPowerComponents[BatteryConsumer.POWER_COMPONENT_COUNT + componentId + - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID] = componentPower; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException( + "Unsupported custom power component ID: " + componentId); + } + return this; + } + + /** + * Creates a read-only object out of the Builder values. + */ + @NonNull + public PowerComponents build() { + return new PowerComponents(this); + } + } +} diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java new file mode 100644 index 000000000000..7dcbf7d4cef3 --- /dev/null +++ b/core/java/android/os/UidBatteryConsumer.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +/** + * Contains power consumption data attributed to a specific UID. + * + * @hide + */ +public final class UidBatteryConsumer extends BatteryConsumer implements Parcelable { + + private final int mUid; + @Nullable + private final String mPackageWithHighestDrain; + + public int getUid() { + return mUid; + } + + @Nullable + public String getPackageWithHighestDrain() { + return mPackageWithHighestDrain; + } + + private UidBatteryConsumer(@NonNull Builder builder) { + super(builder.mPowerComponentsBuilder.build()); + mUid = builder.mUid; + mPackageWithHighestDrain = builder.mPackageWithHighestDrain; + } + + private UidBatteryConsumer(@NonNull Parcel source) { + super(new PowerComponents(source)); + mUid = source.readInt(); + mPackageWithHighestDrain = source.readString(); + } + + /** + * Writes the contents into a Parcel. + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mUid); + dest.writeString(mPackageWithHighestDrain); + } + + @NonNull + public static final Creator<UidBatteryConsumer> CREATOR = new Creator<UidBatteryConsumer>() { + public UidBatteryConsumer createFromParcel(@NonNull Parcel source) { + return new UidBatteryConsumer(source); + } + + public UidBatteryConsumer[] newArray(int size) { + return new UidBatteryConsumer[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + /** + * Builder for UidBatteryConsumer. + */ + public static final class Builder { + private final PowerComponents.Builder mPowerComponentsBuilder; + private final int mUid; + private String mPackageWithHighestDrain; + + public Builder(int customPowerComponentCount, int uid) { + mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentCount); + mUid = uid; + } + + /** + * Creates a read-only object out of the Builder values. + */ + @NonNull + public UidBatteryConsumer build() { + return new UidBatteryConsumer(this); + } + + /** + * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. + * + * @param componentId The ID of the power component, e.g. + * {@link BatteryConsumer#POWER_COMPONENT_CPU}. + * @param componentPower Amount of consumed power in mAh. + */ + @NonNull + public Builder setConsumedPower(@PowerComponent int componentId, double componentPower) { + mPowerComponentsBuilder.setConsumedPower(componentId, componentPower); + return this; + } + + /** + * Sets the amount of drain attributed to the specified custom drain type. + * + * @param componentId The ID of the custom power component. + * @param componentPower Amount of consumed power in mAh. + */ + @NonNull + public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) { + mPowerComponentsBuilder.setConsumedPowerForCustomComponent(componentId, componentPower); + return this; + } + + /** + * Sets the amount of power consumed since BatteryStats reset, mAh. + */ + @NonNull + public Builder setConsumedPower(double consumedPower) { + mPowerComponentsBuilder.setTotalPowerConsumed(consumedPower); + return this; + } + + /** + * Sets the name of the package owned by this UID that consumed the highest amount + * of power since BatteryStats reset. + */ + @NonNull + public Builder setPackageWithHighestDrain(@Nullable String packageName) { + mPackageWithHighestDrain = packageName; + return this; + } + } +} diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl index 100166814975..a94077dd7ad6 100644 --- a/core/java/android/permission/IPermissionManager.aidl +++ b/core/java/android/permission/IPermissionManager.aidl @@ -73,10 +73,6 @@ interface IPermissionManager { void resetRuntimePermissions(); - boolean setDefaultBrowser(String packageName, int userId); - - String getDefaultBrowser(int userId); - void grantDefaultPermissionsToEnabledCarrierApps(in String[] packageNames, int userId); void grantDefaultPermissionsToEnabledImsServices(in String[] packageNames, int userId); @@ -91,10 +87,6 @@ interface IPermissionManager { void revokeDefaultPermissionsFromLuiApps(in String[] packageNames, int userId); - void setPermissionEnforced(String permName, boolean enforced); - - boolean isPermissionEnforced(String permName); - boolean shouldShowRequestPermissionRationale(String permName, String packageName, int userId); diff --git a/core/java/android/permission/PermissionManagerInternal.java b/core/java/android/permission/PermissionManagerInternal.java index 3134ec06fe50..a4676c47bac3 100644 --- a/core/java/android/permission/PermissionManagerInternal.java +++ b/core/java/android/permission/PermissionManagerInternal.java @@ -19,11 +19,6 @@ package android.permission; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.os.UserHandle; - -import com.android.internal.util.function.TriFunction; - -import java.util.function.BiFunction; /** * Internal interfaces to be used by other components within the system server. @@ -32,96 +27,39 @@ import java.util.function.BiFunction; * * @hide */ -public abstract class PermissionManagerInternal { - - /** - * Listener for package permission state (permissions or flags) changes. - */ - public interface OnRuntimePermissionStateChangedListener { - - /** - * Called when the runtime permission state (permissions or flags) changed. - * - * @param packageName The package for which the change happened. - * @param userId the user id for which the change happened. - */ - @Nullable - void onRuntimePermissionStateChanged(@NonNull String packageName, - @UserIdInt int userId); - } - - /** Interface to override permission checks via composition */ - public interface CheckPermissionDelegate { - /** - * Checks whether the given package has been granted the specified permission. - * - * @return If the package has the permission, PERMISSION_GRANTED is - * returned. If it does not have the permission, PERMISSION_DENIED - * is returned. - * - * @see android.content.pm.PackageManager#checkPermission(String, String) - */ - int checkPermission(String permName, String pkgName, int userId, - TriFunction<String, String, Integer, Integer> superImpl); - - /** - /** - * Checks whether the given uid has been granted the specified permission. - * - * @return If the package has the permission, PERMISSION_GRANTED is - * returned. If it does not have the permission, PERMISSION_DENIED - * is returned. - * - */ - int checkUidPermission(String permName, int uid, - BiFunction<String, Integer, Integer> superImpl); - } - +public interface PermissionManagerInternal { /** - * Get the state of the runtime permissions as xml file. + * Get the state of the runtime permissions as a blob. * - * @param user The user the data should be extracted for + * @param userId The user ID the data should be extracted for * - * @return The state as a xml file + * @return the state as a blob */ - public abstract @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user); + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + @Nullable + byte[] backupRuntimePermissions(@UserIdInt int userId); /** * Restore a permission state previously backed up via {@link #backupRuntimePermissions}. + * <p> + * If not all state can be restored, the un-restorable state will be delayed and can be + * retried via {@link #restoreDelayedRuntimePermissions}. * - * <p>If not all state can be restored, the un-restoreable state will be delayed and can be - * re-tried via {@link #restoreDelayedRuntimePermissions}. - * - * @param backup The state as an xml file - * @param user The user the data should be restored for + * @param backup the state as a blob + * @param userId the user ID the data should be restored for */ - public abstract void restoreRuntimePermissions(@NonNull byte[] backup, - @NonNull UserHandle user); + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId); /** * Try to apply permission backup of a package that was previously not applied. * - * @param packageName The package that is newly installed - * @param user The user the package is installed for + * @param packageName the package that is newly installed + * @param userId the user ID the package is installed for * * @see #restoreRuntimePermissions */ - public abstract void restoreDelayedRuntimePermissions(@NonNull String packageName, - @NonNull UserHandle user); - - /** - * Adds a listener for runtime permission state (permissions or flags) changes. - * - * @param listener The listener. - */ - public abstract void addOnRuntimePermissionStateChangedListener( - @NonNull OnRuntimePermissionStateChangedListener listener); - - /** - * Removes a listener for runtime permission state (permissions or flags) changes. - * - * @param listener The listener. - */ - public abstract void removeOnRuntimePermissionStateChangedListener( - @NonNull OnRuntimePermissionStateChangedListener listener); + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + void restoreDelayedRuntimePermissions(@NonNull String packageName, + @UserIdInt int userId); } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 03e8a070b237..da06e821ea9f 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -19,6 +19,7 @@ package android.provider; import android.accounts.Account; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -47,6 +48,9 @@ import android.database.DatabaseUtils; import android.graphics.Rect; import android.net.Uri; import android.os.Build; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.telecom.PhoneAccountHandle; import android.text.TextUtils; @@ -54,11 +58,15 @@ import android.util.DisplayMetrics; import android.util.Pair; import android.view.View; +import com.google.android.collect.Sets; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.Set; /** * <p> @@ -8188,6 +8196,321 @@ public final class ContactsContract { public static final String RAW_CONTACT_ID2 = "raw_contact_id2"; } + + /** + * Class containing utility methods around determine what accounts in the ContactsProvider are + * related to the SIM cards in the device. + * <p> + * Apps interested in managing contacts from SIM cards can query the ContactsProvider using + * {@link #getSimAccounts(ContentResolver)} to get all accounts that relate to SIM cards. They + * can also register a receiver for the {@link #ACTION_SIM_ACCOUNTS_CHANGED} broadcast to be + * notified when these accounts change. + */ + public static final class SimContacts { + /** + * This utility class cannot be instantiated + */ + private SimContacts() { + } + + /** + * The method to invoke in order to add a new SIM account for a newly inserted SIM card. + * + * @hide + */ + public static final String ADD_SIM_ACCOUNT_METHOD = "addSimAccount"; + + /** + * The method to invoke in order to remove a SIM account once the corresponding SIM card is + * ejected. + * + * @hide + */ + public static final String REMOVE_SIM_ACCOUNT_METHOD = "removeSimAccount"; + + /** + * The method to invoke in order to query all SIM accounts. + * + * @hide + */ + public static final String QUERY_SIM_ACCOUNTS_METHOD = "querySimAccounts"; + + /** + * Key to add in the outgoing Bundle for the SIM slot. + * + * @hide + */ + public static final String KEY_SIM_SLOT_INDEX = "key_sim_slot_index"; + + /** + * Key to add in the outgoing Bundle for the SIM account's EF type. + * See {@link SimAccount#mEfType} for more information. + * + * @hide + */ + public static final String KEY_SIM_EF_TYPE = "key_sim_ef_type"; + + /** + * Key to add in the outgoing Bundle for the account name. + * + * @hide + */ + public static final String KEY_ACCOUNT_NAME = "key_sim_account_name"; + + /** + * Key to add in the outgoing Bundle for the account type. + * + * @hide + */ + public static final String KEY_ACCOUNT_TYPE = "key_sim_account_type"; + + /** + * Key in the incoming Bundle for the all the SIM accounts. + * + * @hide + */ + public static final String KEY_SIM_ACCOUNTS = "key_sim_accounts"; + + /** + * Broadcast Action: SIM accounts have changed, call + * {@link #getSimAccounts(ContentResolver)} to get the latest. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SIM_ACCOUNTS_CHANGED = + "android.provider.action.SIM_ACCOUNTS_CHANGED"; + + /** + * Adds a new SIM account that maps to the corresponding SIM slot. + * + * @param accountName accountName value for the account + * @param accountType accountType value for the account + * @param contentResolver to perform the operation on. + * @param simSlotIndex the SIM slot index of this new account. + * @param efType the EF type of this new account. + * @hide + */ + @SystemApi + @RequiresPermission("android.contacts.permission.MANAGE_SIM_ACCOUNTS") + public static void addSimAccount(@NonNull ContentResolver contentResolver, + @NonNull String accountName, + @NonNull String accountType, + int simSlotIndex, + int efType) { + if (simSlotIndex < 0) { + throw new IllegalArgumentException("Sim slot is negative"); + } + if (!SimAccount.getValidEfTypes().contains(efType)) { + throw new IllegalArgumentException("Invalid EF type"); + } + if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { + throw new IllegalArgumentException("Account name or type is empty"); + } + + Bundle extras = new Bundle(); + extras.putInt(KEY_SIM_SLOT_INDEX, simSlotIndex); + extras.putInt(KEY_SIM_EF_TYPE, efType); + extras.putString(KEY_ACCOUNT_NAME, accountName); + extras.putString(KEY_ACCOUNT_TYPE, accountType); + + contentResolver.call(ContactsContract.AUTHORITY_URI, + ContactsContract.SimContacts.ADD_SIM_ACCOUNT_METHOD, + null, extras); + } + + /** + * Removes all SIM accounts that map to the corresponding SIM slot. + * + * @param contentResolver to perform the operation on. + * @param simSlotIndex the SIM slot index of the accounts to remove. + * @hide + */ + @SystemApi + @RequiresPermission("android.contacts.permission.MANAGE_SIM_ACCOUNTS") + public static void removeSimAccounts(@NonNull ContentResolver contentResolver, + int simSlotIndex) { + if (simSlotIndex < 0) { + throw new IllegalArgumentException("Sim slot is negative"); + } + + Bundle extras = new Bundle(); + extras.putInt(KEY_SIM_SLOT_INDEX, simSlotIndex); + + contentResolver.call(ContactsContract.AUTHORITY_URI, + ContactsContract.SimContacts.REMOVE_SIM_ACCOUNT_METHOD, + null, extras); + } + + /** + * Returns all known SIM accounts. May be empty but never null. + * + * @param contentResolver content resolver to query. + */ + public static @NonNull List<SimAccount> getSimAccounts( + @NonNull ContentResolver contentResolver) { + Bundle response = contentResolver.call(ContactsContract.AUTHORITY_URI, + ContactsContract.SimContacts.QUERY_SIM_ACCOUNTS_METHOD, + null, null); + List<SimAccount> result = response.getParcelableArrayList(KEY_SIM_ACCOUNTS); + + if (result == null) { + result = new ArrayList<>(); + } + + return result; + } + } + + /** + * A parcelable class encapsulating account data for contacts that originate from a SIM card. + */ + public static final class SimAccount implements Parcelable { + /** An invalid EF type identifier. */ + public static final int UNKNOWN_EF_TYPE = 0; + /** EF type identifier for the ADN partition. */ + public static final int ADN_EF_TYPE = 1; + /** EF type identifier for the SDN partition. */ + public static final int SDN_EF_TYPE = 2; + /** EF type identifier for the FDN partition. */ + public static final int FDN_EF_TYPE = 3; + + /** + * The account_name of this SIM account. See {@link RawContacts#ACCOUNT_NAME}. + */ + private final String mAccountName; + + /** + * The account_type of this SIM account. See {@link RawContacts#ACCOUNT_TYPE}. + */ + private final String mAccountType; + + /** + * The slot index of the SIM card this account maps to. See {@link + * android.telephony.SubscriptionInfo#getSimSlotIndex()}. + */ + private final int mSimSlotIndex; + + /** + * The EF type of the contacts stored in this account. One of + * {@link #ADN_EF_TYPE}, {@link #SDN_EF_TYPE} or {@link #FDN_EF_TYPE}. + * + * EF type is the Elementary File type of the partition these contacts come from within the + * SIM card. + * + * ADN is the "abbreviated dialing numbers" or the user managed SIM contacts. + * + * SDN is the "service dialing numbers" which are usually preloaded onto the SIM by the + * carrier. + * + * FDN is the "fixed dialing numbers" which are contacts which can only be dialed from that + * SIM, used in cases such as parental control. + */ + private final int mEfType; + + /** + * @return A set containing all known EF type values + * @hide + */ + public static @NonNull Set<Integer> getValidEfTypes() { + return Sets.newArraySet(ADN_EF_TYPE, SDN_EF_TYPE, FDN_EF_TYPE); + } + + /** + * @hide + */ + public SimAccount(@NonNull String accountName, @NonNull String accountType, + int simSlotIndex, + int efType) { + this.mAccountName = accountName; + this.mAccountType = accountType; + this.mSimSlotIndex = simSlotIndex; + this.mEfType = efType; + } + + /** + * @return The account_name of this SIM account. See {@link RawContacts#ACCOUNT_NAME}. + */ + public @NonNull String getAccountName() { + return mAccountName; + } + + /** + * @return The account_type of this SIM account. See {@link RawContacts#ACCOUNT_TYPE}. + */ + public @NonNull String getAccountType() { + return mAccountType; + } + + /** + * @return The slot index of the SIM card this account maps to. See + * {@link android.telephony.SubscriptionInfo#getSimSlotIndex()}. + */ + public int getSimSlotIndex() { + return mSimSlotIndex; + } + + /** + * @return The EF type of the contacts stored in this account. + */ + public int getEfType() { + return mEfType; + } + + @Override + public int hashCode() { + return Objects.hash(mAccountName, mAccountType, mSimSlotIndex, mEfType); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (obj == this) return true; + + SimAccount toCompare; + try { + toCompare = (SimAccount) obj; + } catch (ClassCastException ex) { + return false; + } + + return mSimSlotIndex == toCompare.mSimSlotIndex + && mEfType == toCompare.mEfType + && Objects.equals(mAccountName, toCompare.mAccountName) + && Objects.equals(mAccountType, toCompare.mAccountType); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mAccountName); + dest.writeString(mAccountType); + dest.writeInt(mSimSlotIndex); + dest.writeInt(mEfType); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Parcelable.Creator<SimAccount> CREATOR = + new Parcelable.Creator<SimAccount>() { + @Override + public SimAccount createFromParcel(Parcel source) { + String accountName = source.readString(); + String accountType = source.readString(); + int simSlot = source.readInt(); + int efType = source.readInt(); + SimAccount simAccount = new SimAccount(accountName, accountType, simSlot, + efType); + return simAccount; + } + + @Override + public SimAccount[] newArray(int size) { + return new SimAccount[size]; + } + }; + } + /** * @see Settings */ diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 44cc0f5c330d..f58fa1595f4a 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -126,6 +126,14 @@ public final class DeviceConfig { public static final String NAMESPACE_AUTOFILL = "autofill"; /** + * Namespace for battery saver feature. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_BATTERY_SAVER = "battery_saver"; + + /** * Namespace for blobstore feature that allows apps to share data blobs. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 249a781509ac..12791bcc194b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9797,12 +9797,13 @@ public final class Settings { "use_blast_adapter_sv"; /** - * If {@code true}, vendor provided window manager display settings will be ignored. - * (0 = false, 1 = true) + * Path to the WindowManager display settings file. If unset, the default file path will + * be used. + * * @hide */ - public static final String DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS = - "ignore_vendor_display_settings"; + public static final String DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH = + "wm_display_settings_path"; /** * Whether user has enabled development settings. @@ -9961,13 +9962,13 @@ public final class Settings { * upon going to sleep. It additionally controls whether a playback device attempts to turn * on the connected Audio system when waking up. Supported values are: * <ul> - * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_TO_TV} Upon going to sleep, device + * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_TV} Upon going to sleep, device * sends {@code <Standby>} to TV only. Upon waking up, device does not turn on the Audio * system via {@code <System Audio Mode Request>}.</li> - * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_BROADCAST} Upon going to sleep, + * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_BROADCAST} Upon going to sleep, * device sends {@code <Standby>} to all devices in the network. Upon waking up, device * attempts to turn on the Audio system via {@code <System Audio Mode Request>}.</li> - * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_NONE} Upon going to sleep, device + * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_NONE} Upon going to sleep, device * does not send any {@code <Standby>} message. Upon waking up, device does not turn on the * Audio system via {@code <System Audio Mode Request>}.</li> * </ul> @@ -11716,24 +11717,6 @@ public final class Settings { "battery_saver_device_specific_constants"; /** - * Settings for adaptive Battery Saver mode. Uses the same flags as - * {@link #BATTERY_SAVER_CONSTANTS}. - * - * @hide - */ - public static final String BATTERY_SAVER_ADAPTIVE_CONSTANTS = - "battery_saver_adaptive_constants"; - - /** - * Device specific settings for adaptive Battery Saver mode. Uses the same flags as - * {@link #BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS}. - * - * @hide - */ - public static final String BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS = - "battery_saver_adaptive_device_specific_constants"; - - /** * Battery tip specific settings * This is encoded as a key=value list, separated by commas. Ex: * diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java index 88a78c36d112..61213e6293ba 100644 --- a/core/java/android/service/carrier/CarrierMessagingService.java +++ b/core/java/android/service/carrier/CarrierMessagingService.java @@ -16,6 +16,7 @@ package android.service.carrier; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; @@ -25,6 +26,8 @@ import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; /** @@ -74,6 +77,15 @@ public abstract class CarrierMessagingService extends Service { */ public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE = 0x2; + /** @hide */ + @IntDef(flag = true, prefix = { "RECEIVE_OPTIONS_" }, value = { + RECEIVE_OPTIONS_DEFAULT, + RECEIVE_OPTIONS_DROP, + RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FilterCompleteResult{} + /** * Indicates that an SMS or MMS message was successfully sent. */ @@ -89,6 +101,15 @@ public abstract class CarrierMessagingService extends Service { */ public static final int SEND_STATUS_ERROR = 2; + /** @hide */ + @IntDef(prefix = { "SEND_STATUS_" }, value = { + SEND_STATUS_OK, + SEND_STATUS_RETRY_ON_CARRIER_NETWORK, + SEND_STATUS_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SendResult {} + /** * Successfully downloaded an MMS message. */ @@ -104,10 +125,26 @@ public abstract class CarrierMessagingService extends Service { */ public static final int DOWNLOAD_STATUS_ERROR = 2; + /** @hide */ + @IntDef(prefix = { "DOWNLOAD_STATUS_" }, value = { + DOWNLOAD_STATUS_OK, + DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK, + DOWNLOAD_STATUS_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DownloadResult {} + /** * Flag to request SMS delivery status report. */ - public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; + public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 0x1; + + /** @hide */ + @IntDef(flag = true, prefix = { "SEND_FLAG_" }, value = { + SEND_FLAG_REQUEST_DELIVERY_STATUS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SendRequest {} private final ICarrierMessagingWrapper mWrapper = new ICarrierMessagingWrapper(); diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java index 4ffffc6870cb..197c5a657371 100644 --- a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java +++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java @@ -16,19 +16,25 @@ package android.service.carrier; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.net.Uri; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.telephony.SmsMessage; import com.android.internal.util.Preconditions; import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; /** * Provides basic structure for platform to connect to the carrier messaging service. @@ -46,6 +52,7 @@ import java.util.List; * CarrierMessagingService. * @hide */ +@SystemApi public final class CarrierMessagingServiceWrapper { // Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete // prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized. @@ -53,6 +60,7 @@ public final class CarrierMessagingServiceWrapper { private volatile ICarrierMessagingService mICarrierMessagingService; private Runnable mOnServiceReadyCallback; + private Executor mServiceReadyCallbackExecutor; /** * Binds to the carrier messaging service under package {@code carrierPackageName}. This method @@ -60,18 +68,27 @@ public final class CarrierMessagingServiceWrapper { * * @param context the context * @param carrierPackageName the carrier package name + * @param executor the executor to run the callback. + * @param onServiceReadyCallback the callback when service becomes ready. * @return true upon successfully binding to a carrier messaging service, false otherwise * @hide */ + @SystemApi public boolean bindToCarrierMessagingService(@NonNull Context context, @NonNull String carrierPackageName, + @NonNull @CallbackExecutor Executor executor, @NonNull Runnable onServiceReadyCallback) { Preconditions.checkState(mCarrierMessagingServiceConnection == null); + Objects.requireNonNull(context); + Objects.requireNonNull(carrierPackageName); + Objects.requireNonNull(executor); + Objects.requireNonNull(onServiceReadyCallback); Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE); intent.setPackage(carrierPackageName); mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection(); mOnServiceReadyCallback = onServiceReadyCallback; + mServiceReadyCallbackExecutor = executor; return context.bindService(intent, mCarrierMessagingServiceConnection, Context.BIND_AUTO_CREATE); } @@ -82,11 +99,13 @@ public final class CarrierMessagingServiceWrapper { * @param context the context * @hide */ + @SystemApi public void disposeConnection(@NonNull Context context) { Preconditions.checkNotNull(mCarrierMessagingServiceConnection); context.unbindService(mCarrierMessagingServiceConnection); mCarrierMessagingServiceConnection = null; mOnServiceReadyCallback = null; + mServiceReadyCallbackExecutor = null; } /** @@ -96,26 +115,38 @@ public final class CarrierMessagingServiceWrapper { */ private void onServiceReady(ICarrierMessagingService carrierMessagingService) { mICarrierMessagingService = carrierMessagingService; - if (mOnServiceReadyCallback != null) mOnServiceReadyCallback.run(); + if (mOnServiceReadyCallback != null && mServiceReadyCallbackExecutor != null) { + final long identity = Binder.clearCallingIdentity(); + try { + mServiceReadyCallbackExecutor.execute(mOnServiceReadyCallback); + } finally { + Binder.restoreCallingIdentity(identity); + } + } } /** - * Request filtering an incoming SMS message. + * Request the CarrierMessagingService to process an incoming SMS text or data message. * The service will call callback.onFilterComplete with the filtering result. * * @param pdu the PDUs of the message - * @param format the format of the PDUs, typically "3gpp" or "3gpp2" + * @param format the format of the PDUs, typically "3gpp" or "3gpp2". + * See {@link SmsMessage#FORMAT_3GPP} and {@link SmsMessage#FORMAT_3GPP2} for + * more details. * @param destPort the destination port of a data SMS. It will be -1 for text SMS * @param subId SMS subscription ID of the SIM + * @param executor the executor to run the callback. * @param callback the callback to notify upon completion * @hide */ - public void filterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort, - int subId, @NonNull final CarrierMessagingCallback callback) { + @SystemApi + public void receiveSms(@NonNull MessagePdu pdu, @NonNull @SmsMessage.Format String format, + int destPort, int subId, @NonNull @CallbackExecutor final Executor executor, + @NonNull final CarrierMessagingCallback callback) { if (mICarrierMessagingService != null) { try { mICarrierMessagingService.filterSms(pdu, format, destPort, subId, - new CarrierMessagingCallbackInternal(callback)); + new CarrierMessagingCallbackInternal(callback, executor)); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -130,19 +161,23 @@ public final class CarrierMessagingServiceWrapper { * @param text the text to send * @param subId SMS subscription ID of the SIM * @param destAddress phone number of the recipient of the message - * @param sendSmsFlag flag for sending SMS + * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and + * {@link CarrierMessagingService#SEND_FLAG_REQUEST_DELIVERY_STATUS}. + * @param executor the executor to run the callback. * @param callback the callback to notify upon completion * @hide */ + @SystemApi public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress, - int sendSmsFlag, @NonNull final CarrierMessagingCallback callback) { - if (mICarrierMessagingService != null) { - try { - mICarrierMessagingService.sendTextSms(text, subId, destAddress, sendSmsFlag, - new CarrierMessagingCallbackInternal(callback)); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + @CarrierMessagingService.SendRequest int sendSmsFlag, + @NonNull @CallbackExecutor final Executor executor, + @NonNull final CarrierMessagingCallback callback) { + Objects.requireNonNull(mICarrierMessagingService); + try { + mICarrierMessagingService.sendTextSms(text, subId, destAddress, sendSmsFlag, + new CarrierMessagingCallbackInternal(callback, executor)); + } catch (RemoteException e) { + throw new RuntimeException(e); } } @@ -155,20 +190,24 @@ public final class CarrierMessagingServiceWrapper { * @param subId SMS subscription ID of the SIM * @param destAddress phone number of the recipient of the message * @param destPort port number of the recipient of the message - * @param sendSmsFlag flag for sending SMS + * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and + * {@link CarrierMessagingService#SEND_FLAG_REQUEST_DELIVERY_STATUS}. + * @param executor the executor to run the callback. * @param callback the callback to notify upon completion * @hide */ + @SystemApi public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress, - int destPort, int sendSmsFlag, + int destPort, @CarrierMessagingService.SendRequest int sendSmsFlag, + @NonNull @CallbackExecutor final Executor executor, @NonNull final CarrierMessagingCallback callback) { - if (mICarrierMessagingService != null) { - try { - mICarrierMessagingService.sendDataSms(data, subId, destAddress, destPort, - sendSmsFlag, new CarrierMessagingCallbackInternal(callback)); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + Objects.requireNonNull(mICarrierMessagingService); + try { + mICarrierMessagingService.sendDataSms(data, subId, destAddress, destPort, + sendSmsFlag, new CarrierMessagingCallbackInternal( + callback, executor)); + } catch (RemoteException e) { + throw new RuntimeException(e); } } @@ -180,20 +219,24 @@ public final class CarrierMessagingServiceWrapper { * @param parts the parts of the multi-part text SMS to send * @param subId SMS subscription ID of the SIM * @param destAddress phone number of the recipient of the message - * @param sendSmsFlag flag for sending SMS + * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and + * {@link CarrierMessagingService#SEND_FLAG_REQUEST_DELIVERY_STATUS}. + * @param executor the executor to run the callback. * @param callback the callback to notify upon completion * @hide */ + @SystemApi public void sendMultipartTextSms(@NonNull List<String> parts, int subId, - @NonNull String destAddress, int sendSmsFlag, + @NonNull String destAddress, + @CarrierMessagingService.SendRequest int sendSmsFlag, + @NonNull @CallbackExecutor final Executor executor, @NonNull final CarrierMessagingCallback callback) { - if (mICarrierMessagingService != null) { - try { - mICarrierMessagingService.sendMultipartTextSms(parts, subId, destAddress, - sendSmsFlag, new CarrierMessagingCallbackInternal(callback)); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + Objects.requireNonNull(mICarrierMessagingService); + try { + mICarrierMessagingService.sendMultipartTextSms(parts, subId, destAddress, + sendSmsFlag, new CarrierMessagingCallbackInternal(callback, executor)); + } catch (RemoteException e) { + throw new RuntimeException(e); } } @@ -206,18 +249,20 @@ public final class CarrierMessagingServiceWrapper { * @param subId SMS subscription ID of the SIM * @param location the optional URI to send this MMS PDU. If this is {code null}, * the PDU should be sent to the default MMSC URL. + * @param executor the executor to run the callback. * @param callback the callback to notify upon completion * @hide */ + @SystemApi public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location, + @NonNull @CallbackExecutor final Executor executor, @NonNull final CarrierMessagingCallback callback) { - if (mICarrierMessagingService != null) { - try { - mICarrierMessagingService.sendMms(pduUri, subId, location, - new CarrierMessagingCallbackInternal(callback)); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + Objects.requireNonNull(mICarrierMessagingService); + try { + mICarrierMessagingService.sendMms(pduUri, subId, location, + new CarrierMessagingCallbackInternal(callback, executor)); + } catch (RemoteException e) { + throw new RuntimeException(e); } } @@ -229,18 +274,20 @@ public final class CarrierMessagingServiceWrapper { * @param pduUri the content provider URI of the PDU to be downloaded. * @param subId SMS subscription ID of the SIM * @param location the URI of the message to be downloaded. + * @param executor the executor to run the callback. * @param callback the callback to notify upon completion * @hide */ + @SystemApi public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location, + @NonNull @CallbackExecutor final Executor executor, @NonNull final CarrierMessagingCallback callback) { - if (mICarrierMessagingService != null) { - try { - mICarrierMessagingService.downloadMms(pduUri, subId, location, - new CarrierMessagingCallbackInternal(callback)); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + Objects.requireNonNull(mICarrierMessagingService); + try { + mICarrierMessagingService.downloadMms(pduUri, subId, location, + new CarrierMessagingCallbackInternal(callback, executor)); + } catch (RemoteException e) { + throw new RuntimeException(e); } } @@ -263,19 +310,19 @@ public final class CarrierMessagingServiceWrapper { * {@link CarrierMessagingServiceWrapper}. * @hide */ + @SystemApi public interface CarrierMessagingCallback { - /** - * Response callback for {@link CarrierMessagingServiceWrapper#filterSms}. + * Response callback for {@link CarrierMessagingServiceWrapper#receiveSms}. * @param result a bitmask integer to indicate how the incoming text SMS should be handled * by the platform. Bits set can be * {@link CarrierMessagingService#RECEIVE_OPTIONS_DROP} and * {@link CarrierMessagingService# * RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE}. - * {@see CarrierMessagingService#onReceiveTextSms}. - * @hide + * {@link CarrierMessagingService#onReceiveTextSms}. */ - default void onFilterComplete(int result) { + default void onReceiveSmsComplete( + @CarrierMessagingService.FilterCompleteResult int result) { } @@ -287,10 +334,9 @@ public final class CarrierMessagingServiceWrapper { * and {@link CarrierMessagingService#SEND_STATUS_ERROR}. * @param messageRef message reference of the just-sent message. This field is applicable * only if result is {@link CarrierMessagingService#SEND_STATUS_OK}. - * @hide */ - default void onSendSmsComplete(int result, int messageRef) { - + default void onSendSmsComplete(@CarrierMessagingService.SendResult + int result, int messageRef) { } /** @@ -301,9 +347,9 @@ public final class CarrierMessagingServiceWrapper { * @param messageRefs an array of message references, one for each part of the * multipart SMS. This field is applicable only if result is * {@link CarrierMessagingService#SEND_STATUS_OK}. - * @hide */ - default void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) { + default void onSendMultipartSmsComplete(@CarrierMessagingService.SendResult + int result, @Nullable int[] messageRefs) { } @@ -315,56 +361,62 @@ public final class CarrierMessagingServiceWrapper { * @param sendConfPdu a possibly {code null} SendConf PDU, which confirms that the message * was sent. sendConfPdu is ignored if the {@code result} is not * {@link CarrierMessagingService#SEND_STATUS_OK}. - * @hide */ - default void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) { + default void onSendMmsComplete(@CarrierMessagingService.SendResult + int result, @Nullable byte[] sendConfPdu) { } /** * Response callback for {@link CarrierMessagingServiceWrapper#downloadMms}. - * @param result download status, one of {@link CarrierMessagingService#SEND_STATUS_OK}, - * {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, - * and {@link CarrierMessagingService#SEND_STATUS_ERROR}. - * @hide + * @param result download status, one of {@link CarrierMessagingService#DOWNLOAD_STATUS_OK}, + * {@link CarrierMessagingService#DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK}, + * and {@link CarrierMessagingService#DOWNLOAD_STATUS_ERROR}. */ - default void onDownloadMmsComplete(int result) { + default void onDownloadMmsComplete(@CarrierMessagingService.DownloadResult + int result) { } } private final class CarrierMessagingCallbackInternal extends ICarrierMessagingCallback.Stub { - CarrierMessagingCallback mCarrierMessagingCallback; + final CarrierMessagingCallback mCarrierMessagingCallback; + final Executor mExecutor; - CarrierMessagingCallbackInternal(CarrierMessagingCallback callback) { + CarrierMessagingCallbackInternal(CarrierMessagingCallback callback, + final Executor executor) { mCarrierMessagingCallback = callback; + mExecutor = executor; } @Override public void onFilterComplete(int result) throws RemoteException { - mCarrierMessagingCallback.onFilterComplete(result); + mExecutor.execute(() -> mCarrierMessagingCallback.onReceiveSmsComplete(result)); } @Override public void onSendSmsComplete(int result, int messageRef) throws RemoteException { - mCarrierMessagingCallback.onSendSmsComplete(result, messageRef); + mExecutor.execute(() -> mCarrierMessagingCallback.onSendSmsComplete( + result, messageRef)); } @Override public void onSendMultipartSmsComplete(int result, int[] messageRefs) throws RemoteException { - mCarrierMessagingCallback.onSendMultipartSmsComplete(result, messageRefs); + mExecutor.execute(() -> mCarrierMessagingCallback.onSendMultipartSmsComplete( + result, messageRefs)); } @Override public void onSendMmsComplete(int result, byte[] sendConfPdu) throws RemoteException { - mCarrierMessagingCallback.onSendMmsComplete(result, sendConfPdu); + mExecutor.execute(() -> mCarrierMessagingCallback.onSendMmsComplete( + result, sendConfPdu)); } @Override public void onDownloadMmsComplete(int result) throws RemoteException { - mCarrierMessagingCallback.onDownloadMmsComplete(result); + mExecutor.execute(() -> mCarrierMessagingCallback.onDownloadMmsComplete(result)); } } -}
\ No newline at end of file +} diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index aa9e289345db..440eeb1619f0 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1517,8 +1517,10 @@ public abstract class NotificationListenerService extends Service { */ public static class Ranking { - /** Value signifying that the user has not expressed a per-app visibility override value. - * @hide */ + /** + * Value signifying that the user and device policy manager have not expressed a lockscreen + * visibility override for a notification. + */ public static final int VISIBILITY_NO_OVERRIDE = NotificationManager.VISIBILITY_NO_OVERRIDE; /** @@ -1695,14 +1697,13 @@ public abstract class NotificationListenerService extends Service { } /** - * Returns the user specified visibility for the package that posted - * this notification, or + * Returns the user or device policy manager specified visibility (see + * {@link Notification#VISIBILITY_PRIVATE}, {@link Notification#VISIBILITY_PUBLIC}, + * {@link Notification#VISIBILITY_SECRET}) for this notification, or * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if * no such preference has been expressed. - * @hide */ - @UnsupportedAppUsage - public int getVisibilityOverride() { + public int getLockscreenVisibilityOverride() { return mVisibilityOverride; } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 787a81bac3c0..12d905588e1e 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -45,13 +45,14 @@ import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -515,7 +516,7 @@ public class ZenModeConfig implements Parcelable { } } - public static ZenModeConfig readXml(XmlPullParser parser) + public static ZenModeConfig readXml(TypedXmlPullParser parser) throws XmlPullParserException, IOException { int type = parser.getEventType(); if (type != XmlPullParser.START_TAG) return null; @@ -611,28 +612,28 @@ public class ZenModeConfig implements Parcelable { * @param version uses XML_VERSION if version is null * @throws IOException */ - public void writeXml(XmlSerializer out, Integer version) throws IOException { + public void writeXml(TypedXmlSerializer out, Integer version) throws IOException { out.startTag(null, ZEN_TAG); out.attribute(null, ZEN_ATT_VERSION, version == null ? Integer.toString(XML_VERSION) : Integer.toString(version)); - out.attribute(null, ZEN_ATT_USER, Integer.toString(user)); + out.attributeInt(null, ZEN_ATT_USER, user); out.startTag(null, ALLOW_TAG); - out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls)); - out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers)); - out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages)); - out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders)); - out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents)); - out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom)); - out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom)); - out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms)); - out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia)); - out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem)); - out.attribute(null, ALLOW_ATT_CONV, Boolean.toString(allowConversations)); - out.attribute(null, ALLOW_ATT_CONV_FROM, Integer.toString(allowConversationsFrom)); + out.attributeBoolean(null, ALLOW_ATT_CALLS, allowCalls); + out.attributeBoolean(null, ALLOW_ATT_REPEAT_CALLERS, allowRepeatCallers); + out.attributeBoolean(null, ALLOW_ATT_MESSAGES, allowMessages); + out.attributeBoolean(null, ALLOW_ATT_REMINDERS, allowReminders); + out.attributeBoolean(null, ALLOW_ATT_EVENTS, allowEvents); + out.attributeInt(null, ALLOW_ATT_CALLS_FROM, allowCallsFrom); + out.attributeInt(null, ALLOW_ATT_MESSAGES_FROM, allowMessagesFrom); + out.attributeBoolean(null, ALLOW_ATT_ALARMS, allowAlarms); + out.attributeBoolean(null, ALLOW_ATT_MEDIA, allowMedia); + out.attributeBoolean(null, ALLOW_ATT_SYSTEM, allowSystem); + out.attributeBoolean(null, ALLOW_ATT_CONV, allowConversations); + out.attributeInt(null, ALLOW_ATT_CONV_FROM, allowConversationsFrom); out.endTag(null, ALLOW_TAG); out.startTag(null, DISALLOW_TAG); - out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects)); + out.attributeInt(null, DISALLOW_ATT_VISUAL_EFFECTS, suppressedVisualEffects); out.endTag(null, DISALLOW_TAG); if (manualRule != null) { @@ -651,14 +652,13 @@ public class ZenModeConfig implements Parcelable { } out.startTag(null, STATE_TAG); - out.attribute(null, STATE_ATT_CHANNELS_BYPASSING_DND, - Boolean.toString(areChannelsBypassingDnd)); + out.attributeBoolean(null, STATE_ATT_CHANNELS_BYPASSING_DND, areChannelsBypassingDnd); out.endTag(null, STATE_TAG); out.endTag(null, ZEN_TAG); } - public static ZenRule readRuleXml(XmlPullParser parser) { + public static ZenRule readRuleXml(TypedXmlPullParser parser) { final ZenRule rt = new ZenRule(); rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true); rt.name = parser.getAttributeValue(null, RULE_ATT_NAME); @@ -691,12 +691,12 @@ public class ZenModeConfig implements Parcelable { return rt; } - public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException { - out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled)); + public static void writeRuleXml(ZenRule rule, TypedXmlSerializer out) throws IOException { + out.attributeBoolean(null, RULE_ATT_ENABLED, rule.enabled); if (rule.name != null) { out.attribute(null, RULE_ATT_NAME, rule.name); } - out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode)); + out.attributeInt(null, RULE_ATT_ZEN, rule.zenMode); if (rule.component != null) { out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString()); } @@ -707,7 +707,7 @@ public class ZenModeConfig implements Parcelable { if (rule.conditionId != null) { out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString()); } - out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime)); + out.attributeLong(null, RULE_ATT_CREATION_TIME, rule.creationTime); if (rule.enabler != null) { out.attribute(null, RULE_ATT_ENABLER, rule.enabler); } @@ -717,10 +717,10 @@ public class ZenModeConfig implements Parcelable { if (rule.zenPolicy != null) { writeZenPolicyXml(rule.zenPolicy, out); } - out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified)); + out.attributeBoolean(null, RULE_ATT_MODIFIED, rule.modified); } - public static Condition readConditionXml(XmlPullParser parser) { + public static Condition readConditionXml(TypedXmlPullParser parser) { final Uri id = safeUri(parser, CONDITION_ATT_ID); if (id == null) return null; final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY); @@ -737,21 +737,21 @@ public class ZenModeConfig implements Parcelable { } } - public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException { + public static void writeConditionXml(Condition c, TypedXmlSerializer out) throws IOException { out.attribute(null, CONDITION_ATT_ID, c.id.toString()); out.attribute(null, CONDITION_ATT_SUMMARY, c.summary); out.attribute(null, CONDITION_ATT_LINE1, c.line1); out.attribute(null, CONDITION_ATT_LINE2, c.line2); - out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon)); - out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state)); - out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags)); + out.attributeInt(null, CONDITION_ATT_ICON, c.icon); + out.attributeInt(null, CONDITION_ATT_STATE, c.state); + out.attributeInt(null, CONDITION_ATT_FLAGS, c.flags); } /** * Read the zen policy from xml * Returns null if no zen policy exists */ - public static ZenPolicy readZenPolicyXml(XmlPullParser parser) { + public static ZenPolicy readZenPolicyXml(TypedXmlPullParser parser) { boolean policySet = false; ZenPolicy.Builder builder = new ZenPolicy.Builder(); @@ -845,7 +845,7 @@ public class ZenModeConfig implements Parcelable { /** * Writes zen policy to xml */ - public static void writeZenPolicyXml(ZenPolicy policy, XmlSerializer out) + public static void writeZenPolicyXml(ZenPolicy policy, TypedXmlSerializer out) throws IOException { writeZenPolicyState(ALLOW_ATT_CALLS_FROM, policy.getPriorityCallSenders(), out); writeZenPolicyState(ALLOW_ATT_MESSAGES_FROM, policy.getPriorityMessageSenders(), out); @@ -868,16 +868,16 @@ public class ZenModeConfig implements Parcelable { out); } - private static void writeZenPolicyState(String attr, int val, XmlSerializer out) + private static void writeZenPolicyState(String attr, int val, TypedXmlSerializer out) throws IOException { if (Objects.equals(attr, ALLOW_ATT_CALLS_FROM) || Objects.equals(attr, ALLOW_ATT_MESSAGES_FROM)) { if (val != ZenPolicy.PEOPLE_TYPE_UNSET) { - out.attribute(null, attr, Integer.toString(val)); + out.attributeInt(null, attr, val); } } else { if (val != ZenPolicy.STATE_UNSET) { - out.attribute(null, attr, Integer.toString(val)); + out.attributeInt(null, attr, val); } } } @@ -894,15 +894,16 @@ public class ZenModeConfig implements Parcelable { return source >= SOURCE_ANYONE && source <= MAX_SOURCE; } - private static Boolean unsafeBoolean(XmlPullParser parser, String att) { - final String val = parser.getAttributeValue(null, att); - if (TextUtils.isEmpty(val)) return null; - return Boolean.parseBoolean(val); + private static Boolean unsafeBoolean(TypedXmlPullParser parser, String att) { + try { + return parser.getAttributeBoolean(null, att); + } catch (Exception e) { + return null; + } } - private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) { - final String val = parser.getAttributeValue(null, att); - return safeBoolean(val, defValue); + private static boolean safeBoolean(TypedXmlPullParser parser, String att, boolean defValue) { + return parser.getAttributeBoolean(null, att, defValue); } private static boolean safeBoolean(String val, boolean defValue) { @@ -910,24 +911,23 @@ public class ZenModeConfig implements Parcelable { return Boolean.parseBoolean(val); } - private static int safeInt(XmlPullParser parser, String att, int defValue) { - final String val = parser.getAttributeValue(null, att); - return tryParseInt(val, defValue); + private static int safeInt(TypedXmlPullParser parser, String att, int defValue) { + return parser.getAttributeInt(null, att, defValue); } - private static ComponentName safeComponentName(XmlPullParser parser, String att) { + private static ComponentName safeComponentName(TypedXmlPullParser parser, String att) { final String val = parser.getAttributeValue(null, att); if (TextUtils.isEmpty(val)) return null; return ComponentName.unflattenFromString(val); } - private static Uri safeUri(XmlPullParser parser, String att) { + private static Uri safeUri(TypedXmlPullParser parser, String att) { final String val = parser.getAttributeValue(null, att); if (TextUtils.isEmpty(val)) return null; return Uri.parse(val); } - private static long safeLong(XmlPullParser parser, String att, long defValue) { + private static long safeLong(TypedXmlPullParser parser, String att, long defValue) { final String val = parser.getAttributeValue(null, att); return tryParseLong(val, defValue); } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index 479a0c16c747..ac6208b1714c 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -17,7 +17,10 @@ package android.telephony; import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.compat.annotation.ChangeId; @@ -28,17 +31,27 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; import android.telephony.Annotation.CallState; +import android.telephony.Annotation.DataActivityType; +import android.telephony.Annotation.DisconnectCauses; +import android.telephony.Annotation.NetworkType; +import android.telephony.Annotation.PreciseDisconnectCauses; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SimActivationState; import android.telephony.Annotation.SrvccState; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; +import android.telephony.NetworkRegistrationInfo.Domain; +import android.telephony.TelephonyManager.DataEnabledReason; +import android.telephony.TelephonyManager.DataState; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IPhoneStateListener; import dalvik.system.VMRuntime; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; @@ -113,7 +126,9 @@ public class PhoneStateListener { * * @see #onServiceStateChanged * @see ServiceState + * @deprecated Use {@link ServiceStateChangedListener} instead. */ + @Deprecated public static final int LISTEN_SERVICE_STATE = 0x00000001; /** @@ -121,8 +136,7 @@ public class PhoneStateListener { * {@more} * * @see #onSignalStrengthChanged - * - * @deprecated by {@link #LISTEN_SIGNAL_STRENGTHS} + * @deprecated Use {@link SignalStrengthsChangedListener} instead. */ @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 0x00000002; @@ -138,7 +152,9 @@ public class PhoneStateListener { * voicemail icon. * * @see #onMessageWaitingIndicatorChanged + * @deprecated Use {@link MessageWaitingIndicatorChangedListener} instead. */ + @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004; /** @@ -149,7 +165,9 @@ public class PhoneStateListener { * {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onCallForwardingIndicatorChanged + * @deprecated Use {@link CallForwardingIndicatorChangedListener} instead. */ + @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008; /** @@ -165,7 +183,9 @@ public class PhoneStateListener { * instead. * * @see #onCellLocationChanged + * @deprecated Use {@link CellLocationChangedListener} instead. */ + @Deprecated public static final int LISTEN_CELL_LOCATION = 0x00000010; /** @@ -173,14 +193,18 @@ public class PhoneStateListener { * {@more} * * @see #onCallStateChanged + * @deprecated Use {@link CallStateChangedListener} instead. */ + @Deprecated public static final int LISTEN_CALL_STATE = 0x00000020; /** * Listen for changes to the data connection state (cellular). * * @see #onDataConnectionStateChanged + * @deprecated Use {@link DataConnectionStateChangedListener} instead. */ + @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040; /** @@ -191,7 +215,9 @@ public class PhoneStateListener { * data-traffic icon. * * @see #onDataActivity + * @deprecated Use {@link DataActivityListener} instead. */ + @Deprecated public static final int LISTEN_DATA_ACTIVITY = 0x00000080; /** @@ -201,7 +227,9 @@ public class PhoneStateListener { * icon. * * @see #onSignalStrengthsChanged + * @deprecated Use {@link SignalStrengthsChangedListener} instead. */ + @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100; /** @@ -211,7 +239,9 @@ public class PhoneStateListener { * @see #onSignalStrengthsChanged * * @hide + * @deprecated Use {@link AlwaysReportedSignalStrengthChangedListener} instead. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 0x00000200; @@ -222,7 +252,9 @@ public class PhoneStateListener { * permission. * * @see #onCellInfoChanged + * @deprecated Use {@link CellInfoChangedListener} instead. */ + @Deprecated public static final int LISTEN_CELL_INFO = 0x00000400; /** @@ -234,8 +266,10 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @hide + * @deprecated Use {@link PreciseCallStateChangedListener} instead. */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @SystemApi public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800; @@ -247,8 +281,10 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onPreciseDataConnectionStateChanged + * @deprecated Use {@link PreciseDataConnectionStateChangedListener} instead. */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000; /** @@ -258,7 +294,7 @@ public class PhoneStateListener { * READ_PRECISE_PHONE_STATE} * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo) * - * @deprecated Use {@link TelephonyManager#getModemActivityInfo()} + * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} instead. * @hide */ @Deprecated @@ -271,7 +307,9 @@ public class PhoneStateListener { * * @see #onServiceStateChanged(ServiceState) * @hide + * @deprecated Use {@link SrvccStateChangedListener} instead. */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 0x00004000; @@ -289,10 +327,11 @@ public class PhoneStateListener { /** * Listen for carrier network changes indicated by a carrier app. * - * @see #onCarrierNetworkRequest - * @see TelephonyManager#notifyCarrierNetworkChange(boolean) + * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean) * @hide + * @deprecated Use {@link CarrierNetworkChangeListener} instead. */ + @Deprecated public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000; /** @@ -311,7 +350,9 @@ public class PhoneStateListener { * * @see #onVoiceActivationStateChanged * @hide + * @deprecated Use {@link VoiceActivationStateChangedListener} instead. */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 0x00020000; @@ -323,20 +364,24 @@ public class PhoneStateListener { * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN - * {@more} + * * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been * fully activated * * @see #onDataActivationStateChanged * @hide + * @deprecated Use {@link DataActivationStateChangedListener} instead. */ + @Deprecated public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000; /** * Listen for changes to the user mobile data state * * @see #onUserMobileDataStateChanged + * @deprecated Use {@link UserMobileDataStateChangedListener} instead. */ + @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000; /** @@ -347,7 +392,9 @@ public class PhoneStateListener { * {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onDisplayInfoChanged + * @deprecated Use {@link DisplayInfoChangedListener} instead. */ + @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000; /** @@ -355,7 +402,9 @@ public class PhoneStateListener { * * @see #onPhoneCapabilityChanged * @hide + * @deprecated Use {@link PhoneCapabilityChangedListener} instead. */ + @Deprecated public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000; /** @@ -365,17 +414,19 @@ public class PhoneStateListener { * subscription user selected as default data subscription in DSDS mode. * * @see #onActiveDataSubscriptionIdChanged + * @deprecated Use {@link ActiveDataSubscriptionIdChangedListener} instead. */ + @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000; /** * Listen for changes to the radio power state. * - * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} - * * @see #onRadioPowerStateChanged * @hide + * @deprecated Use {@link RadioPowerStateChangedListener} instead. */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 0x00800000; @@ -385,7 +436,10 @@ public class PhoneStateListener { * * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @deprecated Use {@link EmergencyNumberListChangedListener} instead. */ + @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000; /** @@ -396,8 +450,10 @@ public class PhoneStateListener { * or the calling app has carrier privileges * (see {@link TelephonyManager#hasCarrierPrivileges}). * + * @deprecated Use {@link CallDisconnectCauseChangedListener} instead. */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000; /** @@ -409,9 +465,11 @@ public class PhoneStateListener { * * @see #onCallAttributesChanged * @hide + * @deprecated Use {@link CallAttributesChangedListener} instead. */ + @Deprecated @SystemApi - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 0x04000000; /** @@ -423,18 +481,20 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo) + * @deprecated Use {@link ImsCallDisconnectCauseChangedListener} instead. */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 0x08000000; /** * Listen for the emergency number placed from an outgoing call. * - * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} - * * @see #onOutgoingEmergencyCall * @hide + * @deprecated Use {@link OutgoingEmergencyCallListener} instead. */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 0x10000000; @@ -442,11 +502,11 @@ public class PhoneStateListener { /** * Listen for the emergency number placed from an outgoing SMS. * - * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} - * * @see #onOutgoingEmergencySms * @hide + * @deprecated Use {@link OutgoingEmergencySmsListener} instead. */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 0x20000000; @@ -465,7 +525,9 @@ public class PhoneStateListener { * of whether the calling app has carrier privileges. * * @see #onRegistrationFailed + * @deprecated Use {@link RegistrationFailedListener} instead. */ + @Deprecated @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000; @@ -479,19 +541,525 @@ public class PhoneStateListener { * of whether the calling app has carrier privileges. * * @see #onBarringInfoChanged + * @deprecated Use {@link BarringInfoChangedListener} instead. */ + @Deprecated @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = 0x80000000; /** - * Listen for changes to the physical channel configuration. + * Event for changes to the network service state (cellular). + * + * @see ServiceStateChangedListener#onServiceStateChanged + * @see ServiceState + * + * @hide + */ + @SystemApi + public static final int EVENT_SERVICE_STATE_CHANGED = 1; + + /** + * Event for changes to the network signal strength (cellular). + * + * @see SignalStrengthsChangedListener#onSignalStrengthsChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; + + /** + * Event for changes to the message-waiting indicator. + * + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that + * the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * <p> + * Example: The status bar uses this to determine when to display the + * voicemail icon. + * + * @see MessageWaitingIndicatorChangedListener#onMessageWaitingIndicatorChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; + + /** + * Event for changes to the call-forwarding indicator. + * + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that + * the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see CallForwardingIndicatorChangedListener#onCallForwardingIndicatorChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; + + /** + * Event for changes to the device's cell location. Note that + * this will result in frequent callbacks to the listener. + * + * If you need regular location updates but want more control over + * the update interval or location precision, you can set up a listener + * through the {@link android.location.LocationManager location manager} + * instead. + * + * @see CellLocationChangedListener#onCellLocationChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public static final int EVENT_CELL_LOCATION_CHANGED = 5; + + /** + * Event for changes to the device call state. + * + * @see CallStateChangedListener#onCallStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) + public static final int EVENT_CALL_STATE_CHANGED = 6; + + /** + * Event for changes to the data connection state (cellular). + * + * @see DataConnectionStateChangedListener#onDataConnectionStateChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; + + /** + * Event for changes to the direction of data traffic on the data + * connection (cellular). + * + * Example: The status bar uses this to display the appropriate + * data-traffic icon. + * + * @see DataActivityListener#onDataActivity + * + * @hide + */ + @SystemApi + public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; + + /** + * Event for changes to the network signal strengths (cellular). + * <p> + * Example: The status bar uses this to control the signal-strength + * icon. + * + * @see SignalStrengthsChangedListener#onSignalStrengthsChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; + + /** + * Event for changes of the network signal strengths (cellular) always reported from modem, + * even in some situations such as the screen of the device is off. + * + * @see AlwaysReportedSignalStrengthChangedListener#onSignalStrengthsChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) + public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; + + /** + * Event for changes to observed cell info. + * + * @see CellInfoChangedListener#onCellInfoChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public static final int EVENT_CELL_INFO_CHANGED = 11; + + /** + * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing, + * background and foreground calls. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see PreciseCallStateChangedListener#onPreciseCallStateChanged * - * @see #onPhysicalChannelConfigurationChanged * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x100000000L; + public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; + + /** + * Event for {@link PreciseDataConnectionState} on the data connection (cellular). + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see PreciseDataConnectionStateChangedListener#onPreciseDataConnectionStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; + + /** + * Event for real time info for all data connections (cellular)). + * + * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo) + * + * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; + + /** + * Event for OEM hook raw event + * + * @see #onOemHookRawEvent + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_OEM_HOOK_RAW = 15; + + /** + * Event for changes to the SRVCC state of the active call. + * + * @see SrvccStateChangedListener#onSrvccStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_SRVCC_STATE_CHANGED = 16; + + /** + * Event for carrier network changes indicated by a carrier app. + * + * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean) + * @see CarrierNetworkChangeListener#onCarrierNetworkChange + * + * @hide + */ + @SystemApi + public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; + + /** + * Event for changes to the sim voice activation state + * + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED + * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN + * + * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been + * fully activated + * + * @see VoiceActivationStateChangedListener#onVoiceActivationStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; + + /** + * Event for changes to the sim data activation state + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED + * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN + * + * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been + * fully activated + * + * @see DataActivationStateChangedListener#onDataActivationStateChanged + * @hide + */ + @SystemApi + public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; + + /** + * Event for changes to the user mobile data state + * + * @see UserMobileDataStateChangedListener#onUserMobileDataStateChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; + + /** + * Event for display info changed event. + * + * @see DisplayInfoChangedListener#onDisplayInfoChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_DISPLAY_INFO_CHANGED = 21; + + /** + * Event for changes to the phone capability. + * + * @see PhoneCapabilityChangedListener#onPhoneCapabilityChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; + + /** + * Event for changes to active data subscription ID. Active data subscription is + * the current subscription used to setup Cellular Internet data. For example, + * it could be the current active opportunistic subscription in use, or the + * subscription user selected as default data subscription in DSDS mode. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling + * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see ActiveDataSubscriptionIdChangedListener#onActiveDataSubscriptionIdChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; + + /** + * Event for changes to the radio power state. + * + * @see RadioPowerStateChangedListener#onRadioPowerStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; + + /** + * Event for changes to emergency number list based on all active subscriptions. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling + * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see EmergencyNumberListChangedListener#onEmergencyNumberListChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; + + /** + * Event for call disconnect causes which contains {@link DisconnectCause} and + * {@link PreciseDisconnectCause}. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see CallDisconnectCauseChangedListener#onCallDisconnectCauseChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; + + /** + * Event for changes to the call attributes of a currently active call. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see CallAttributesChangedListener#onCallAttributesChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; + + /** + * Event for IMS call disconnect causes which contains + * {@link android.telephony.ims.ImsReasonInfo} + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see ImsCallDisconnectCauseChangedListener#onImsCallDisconnectCauseChanged(ImsReasonInfo) + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; + + /** + * Event for the emergency number placed from an outgoing call. + * + * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; + + /** + * Event for the emergency number placed from an outgoing SMS. + * + * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; + + /** + * Event for registration failures. + * + * Event for indications that a registration procedure has failed in either the CS or PS + * domain. This indication does not necessarily indicate a change of service state, which should + * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or + * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless + * of whether the calling app has carrier privileges. + * + * @see RegistrationFailedListener#onRegistrationFailed + * + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public static final int EVENT_REGISTRATION_FAILURE = 31; + + /** + * Event for Barring Information for the current registered / camped cell. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or + * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless + * of whether the calling app has carrier privileges. + * + * @see BarringInfoChangedListener#onBarringInfoChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public static final int EVENT_BARRING_INFO_CHANGED = 32; + + /** + * Event for changes to the physical channel configuration. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see PhysicalChannelConfigChangedListener#onPhysicalChannelConfigChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; + + + /** + * Event for changes to the data enabled. + * + * Event for indications that the enabled status of current data has changed. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see DataEnabledChangedListener#onDataEnabledChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_DATA_ENABLED_CHANGED = 34; + + /** @hide */ + @IntDef(prefix = { "EVENT_" }, value = { + EVENT_SERVICE_STATE_CHANGED, + EVENT_SIGNAL_STRENGTH_CHANGED, + EVENT_MESSAGE_WAITING_INDICATOR_CHANGED, + EVENT_CALL_FORWARDING_INDICATOR_CHANGED, + EVENT_CELL_LOCATION_CHANGED, + EVENT_CALL_STATE_CHANGED, + EVENT_DATA_CONNECTION_STATE_CHANGED, + EVENT_DATA_ACTIVITY_CHANGED, + EVENT_SIGNAL_STRENGTHS_CHANGED, + EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED, + EVENT_CELL_INFO_CHANGED, + EVENT_PRECISE_CALL_STATE_CHANGED, + EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED, + EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED, + EVENT_OEM_HOOK_RAW, + EVENT_SRVCC_STATE_CHANGED, + EVENT_CARRIER_NETWORK_CHANGED, + EVENT_VOICE_ACTIVATION_STATE_CHANGED, + EVENT_DATA_ACTIVATION_STATE_CHANGED, + EVENT_USER_MOBILE_DATA_STATE_CHANGED, + EVENT_DISPLAY_INFO_CHANGED, + EVENT_PHONE_CAPABILITY_CHANGED, + EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED, + EVENT_RADIO_POWER_STATE_CHANGED, + EVENT_EMERGENCY_NUMBER_LIST_CHANGED, + EVENT_CALL_DISCONNECT_CAUSE_CHANGED, + EVENT_CALL_ATTRIBUTES_CHANGED, + EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED, + EVENT_OUTGOING_EMERGENCY_CALL, + EVENT_OUTGOING_EMERGENCY_SMS, + EVENT_REGISTRATION_FAILURE, + EVENT_BARRING_INFO_CHANGED, + EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, + EVENT_DATA_ENABLED_CHANGED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TelephonyEvent {} /* * Subscription used to listen to the phone state changes @@ -504,9 +1072,13 @@ public class PhoneStateListener { /** * @hide */ + //TODO: The maxTargetSdk should be S if the build time tool updates it. @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - @UnsupportedAppUsage - public final IPhoneStateListener callback; + @UnsupportedAppUsage( + maxTargetSdk = Build.VERSION_CODES.R, + publicAlternatives = "Use {@code TelephonyManager#registerPhoneStateListener(" + + "Executor, PhoneStateListener)} instead") + public IPhoneStateListener callback; /** * Create a PhoneStateListener for the Phone with the default subscription. @@ -563,17 +1135,742 @@ public class PhoneStateListener { * The Executor must not be null. * * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener. + * @deprecated Use + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} instead. */ + @Deprecated public PhoneStateListener(@NonNull Executor executor) { this(null, executor); } - private PhoneStateListener(Integer subId, Executor e) { - if (e == null) { + private @NonNull Executor mExecutor; + + /** + * @hide + */ + public void setExecutor(@NonNull @CallbackExecutor Executor executor) { + if (executor == null) { throw new IllegalArgumentException("PhoneStateListener Executor must be non-null"); } + mExecutor = executor; + } + + private PhoneStateListener(Integer subId, Executor executor) { + setExecutor(executor); mSubId = subId; - callback = new IPhoneStateListenerStub(this, e); + callback = new IPhoneStateListenerStub(this, mExecutor); + } + + /** + * Interface for service state listener. + */ + public interface ServiceStateChangedListener { + /** + * Callback invoked when device service state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * The instance of {@link ServiceState} passed as an argument here will have various + * levels of location information stripped from it depending on the location permissions + * that your app holds. + * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will + * receive all the information in {@link ServiceState}. + * + * @see ServiceState#STATE_EMERGENCY_ONLY + * @see ServiceState#STATE_IN_SERVICE + * @see ServiceState#STATE_OUT_OF_SERVICE + * @see ServiceState#STATE_POWER_OFF + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onServiceStateChanged(@NonNull ServiceState serviceState); + } + + /** + * Interface for message waiting indicator listener. + */ + public interface MessageWaitingIndicatorChangedListener { + /** + * Callback invoked when the message-waiting indicator changes on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onMessageWaitingIndicatorChanged(boolean mwi); + } + + /** + * Interface for call-forwarding indicator listener. + */ + public interface CallForwardingIndicatorChangedListener { + /** + * Callback invoked when the call-forwarding indicator changes on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onCallForwardingIndicatorChanged(boolean cfi); + } + + /** + * Interface for device cell location listener. + */ + public interface CellLocationChangedListener { + /** + * Callback invoked when device cell location changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public void onCellLocationChanged(@NonNull CellLocation location); + } + + /** + * Interface for call state listener. + */ + public interface CallStateChangedListener { + /** + * Callback invoked when device call state changes. + * <p> + * Reports the state of Telephony (mobile) calls on the device for the registered s + * ubscription. + * <p> + * Note: the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * <p> + * Note: The state returned here may differ from that returned by + * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that + * calling {@link TelephonyManager#getCallState()} from within this callback may return a + * different state than the callback reports. + * + * @param state call state + * @param phoneNumber call phone number. If application does not have + * {@link android.Manifest.permission#READ_CALL_LOG} permission or carrier + * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be + * passed as an argument. + */ + @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) + public void onCallStateChanged(@CallState int state, @Nullable String phoneNumber); + } + + /** + * Interface for data connection state listener. + */ + public interface DataConnectionStateChangedListener { + /** + * Callback invoked when connection state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @see TelephonyManager#DATA_DISCONNECTED + * @see TelephonyManager#DATA_CONNECTING + * @see TelephonyManager#DATA_CONNECTED + * @see TelephonyManager#DATA_SUSPENDED + * + * @param state is the current state of data connection. + * @param networkType is the current network type of data connection. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onDataConnectionStateChanged(@DataState int state, + @NetworkType int networkType); + } + + /** + * Interface for data activity state listener. + */ + public interface DataActivityListener { + /** + * Callback invoked when data activity state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @see TelephonyManager#DATA_ACTIVITY_NONE + * @see TelephonyManager#DATA_ACTIVITY_IN + * @see TelephonyManager#DATA_ACTIVITY_OUT + * @see TelephonyManager#DATA_ACTIVITY_INOUT + * @see TelephonyManager#DATA_ACTIVITY_DORMANT + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onDataActivity(@DataActivityType int direction); + } + + /** + * Interface for network signal strengths listener. + */ + public interface SignalStrengthsChangedListener { + /** + * Callback invoked when network signal strengths changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength); + } + + /** + * Interface for network signal strengths listener which always reported from modem. + */ + public interface AlwaysReportedSignalStrengthChangedListener { + /** + * Callback always invoked from modem when network signal strengths changes on the + * registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) + public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength); + } + + /** + * Interface for cell info listener. + */ + public interface CellInfoChangedListener { + /** + * Callback invoked when a observed cell info has changed or new cells have been added + * or removed on the registered subscription. + * Note, the registration subscription ID s from {@link TelephonyManager} object + * which registersPhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param cellInfo is the list of currently visible cells. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo); + } + + /** + * Interface for precise device call state listener. + * + * @hide + */ + @SystemApi + public interface PreciseCallStateChangedListener { + /** + * Callback invoked when precise device call state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param callState {@link PreciseCallState} + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onPreciseCallStateChanged(@NonNull PreciseCallState callState); + } + + /** + * Interface for call disconnect cause listener. + */ + public interface CallDisconnectCauseChangedListener { + /** + * Callback invoked when call disconnect cause changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param disconnectCause {@link DisconnectCause}. + * @param preciseDisconnectCause {@link PreciseDisconnectCause}. + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause, + @PreciseDisconnectCauses int preciseDisconnectCause); + } + + /** + * Interface for IMS call disconnect cause listener. + */ + public interface ImsCallDisconnectCauseChangedListener { + /** + * Callback invoked when IMS call disconnect cause changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed. + * + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo); + } + + /** + * Interface for precise data connection state listener. + */ + public interface PreciseDataConnectionStateChangedListener { + /** + * Callback providing update about the default/internet data connection on the registered + * subscription. + * + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @param dataConnectionState {@link PreciseDataConnectionState} + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onPreciseDataConnectionStateChanged( + @NonNull PreciseDataConnectionState dataConnectionState); + } + + /** + * Interface for Single Radio Voice Call Continuity listener. + * + * @hide + */ + @SystemApi + public interface SrvccStateChangedListener { + /** + * Callback invoked when there has been a change in the Single Radio Voice Call Continuity + * (SRVCC) state for the currently active call on the registered subscription. + * + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onSrvccStateChanged(@SrvccState int srvccState); + } + + /** + * Interface for SIM voice activation state listener. + * + * @hide + */ + @SystemApi + public interface VoiceActivationStateChangedListener { + /** + * Callback invoked when the SIM voice activation state has changed on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param state is the current SIM voice activation state + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onVoiceActivationStateChanged(@SimActivationState int state); + + } + + /** + * Interface for SIM data activation state listener. + */ + public interface DataActivationStateChangedListener { + /** + * Callback invoked when the SIM data activation state has changed on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param state is the current SIM data activation state + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onDataActivationStateChanged(@SimActivationState int state); + } + + /** + * Interface for user mobile data state listener. + */ + public interface UserMobileDataStateChangedListener { + /** + * Callback invoked when the user mobile data state has changed on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param enabled indicates whether the current user mobile data state is enabled or + * disabled. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onUserMobileDataStateChanged(boolean enabled); + } + + /** + * Interface for display info listener. + */ + public interface DisplayInfoChangedListener { + /** + * Callback invoked when the display info has changed on the registered subscription. + * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user + * based on carrier policy. + * + * @param telephonyDisplayInfo The display information. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo); + } + + /** + * Interface for the current emergency number list listener. + */ + public interface EmergencyNumberListChangedListener { + /** + * Callback invoked when the current emergency number list has changed on the registered + * subscription. + * + * Note, the registered subscription is associated with {@link TelephonyManager} object + * on which + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} + * was called. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * given subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param emergencyNumberList Map associating all active subscriptions on the device with + * the list of emergency numbers originating from that + * subscription. + * If there are no active subscriptions, the map will contain a + * single entry with + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as + * the key and a list of emergency numbers as the value. If no + * emergency number information is available, the value will be + * empty. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onEmergencyNumberListChanged( + @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList); + } + + /** + * Interface for outgoing emergency call listener. + * + * @hide + */ + @SystemApi + public interface OutgoingEmergencyCallListener { + /** + * Callback invoked when an outgoing call is placed to an emergency number. + * + * This method will be called when an emergency call is placed on any subscription + * (including the no-SIM case), regardless of which subscription this listener was + * registered on. + * + * The default implementation of this method calls + * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes. + * Do not call {@code super(...)} from within your implementation unless you want + * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well. + * + * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was + * placed to. + * @param subscriptionId The subscription ID used to place the emergency call. If the + * emergency call was placed without a valid subscription + * (e.g. when there are no SIM cards in the device), this will be + * equal to {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + */ + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber, + int subscriptionId); + } + + /** + * Interface for outgoing emergency sms listener. + * + * @hide + */ + @SystemApi + public interface OutgoingEmergencySmsListener { + /** + * Smsback invoked when an outgoing sms is sent to an emergency number. + * + * This method will be called when an emergency sms is sent on any subscription, + * regardless of which subscription this listener was registered on. + * + * The default implementation of this method calls + * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do + * not call {@code super(...)} from within your implementation unless you want + * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well. + * + * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to. + * @param subscriptionId The subscription ID used to send the emergency sms. + */ + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber, + int subscriptionId); + } + + /** + * Interface for phone capability listener. + * + */ + public interface PhoneCapabilityChangedListener { + /** + * Callback invoked when phone capability changes. + * Note, this callback triggers regardless of registered subscription. + * + * @param capability the new phone capability + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability); + } + + /** + * Interface for active data subscription ID listener. + */ + public interface ActiveDataSubscriptionIdChangedListener { + /** + * Callback invoked when active data subscription ID changes. + * Note, this callback triggers regardless of registered subscription. + * + * @param subId current subscription used to setup Cellular Internet data. + * For example, it could be the current active opportunistic subscription + * in use, or the subscription user selected as default data subscription in + * DSDS mode. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onActiveDataSubscriptionIdChanged(int subId); + } + + /** + * Interface for modem radio power state listener. + * + * @hide + */ + @SystemApi + public interface RadioPowerStateChangedListener { + /** + * Callback invoked when modem radio power state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param state the modem radio power state + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onRadioPowerStateChanged(@RadioPowerState int state); + } + + /** + * Interface for carrier network listener. + */ + public interface CarrierNetworkChangeListener { + /** + * Callback invoked when telephony has received notice from a carrier + * app that a network action that could result in connectivity loss + * has been requested by an app using + * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)} + * + * This is optional and is only used to allow the system to provide alternative UI while + * telephony is performing an action that may result in intentional, temporary network + * lack of connectivity. + * + * Note, this callback is pinned to the registered subscription and will be invoked when + * the notifying carrier app has carrier privilege rule on the registered + * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges} + * + * @param active If the carrier network change is or shortly will be active, + * {@code true} indicate that showing alternative UI, {@code false} otherwise. + */ + public void onCarrierNetworkChange(boolean active); + } + + /** + * Interface for registration failures listener. + */ + public interface RegistrationFailedListener { + /** + * Report that Registration or a Location/Routing/Tracking Area update has failed. + * + * <p>Indicate whenever a registration procedure, including a location, routing, or tracking + * area update fails. This includes procedures that do not necessarily result in a change of + * the modem's registration status. If the modem's registration status changes, that is + * reflected in the onNetworkStateChanged() and subsequent + * get{Voice/Data}RegistrationState(). + * + * <p>Because registration failures are ephemeral, this callback is not sticky. + * Registrants will not receive the most recent past value when registering. + * + * @param cellIdentity the CellIdentity, which must include the globally unique identifier + * for the cell (for example, all components of the CGI or ECGI). + * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the + * cell that was chosen for the failed registration attempt. + * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure. + * @param causeCode the primary failure cause code of the procedure. + * For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95 + * For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147 + * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9 + * For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2 + * Integer.MAX_VALUE if this value is unused. + * @param additionalCauseCode the cause code of any secondary/combined procedure + * if appropriate. For UMTS, if a combined attach succeeds for + * PS only, then the GMM cause code shall be included as an + * additionalCauseCode. For LTE (ESM), cause codes are in + * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused. + */ + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, + @NonNull String chosenPlmn, @Domain int domain, + int causeCode, int additionalCauseCode); + } + + /** + * Interface for call attributes listener. + * + * @hide + */ + @SystemApi + public interface CallAttributesChangedListener { + /** + * Callback invoked when the call attributes changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param callAttributes the call attributes + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + void onCallAttributesChanged(@NonNull CallAttributes callAttributes); + } + + /** + * Interface for barring information listener. + */ + public interface BarringInfoChangedListener { + /** + * Report updated barring information for the current camped/registered cell. + * + * <p>Barring info is provided for all services applicable to the current camped/registered + * cell, for the registered PLMN and current access class/access category. + * + * @param barringInfo for all services on the current cell. + * @see android.telephony.BarringInfo + */ + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public void onBarringInfoChanged(@NonNull BarringInfo barringInfo); + } + + /** + * Interface for current physical channel configuration listener. + * @hide + */ + @SystemApi + public interface PhysicalChannelConfigChangedListener { + /** + * Callback invoked when the current physical channel configuration has changed + * + * @param configs List of the current {@link PhysicalChannelConfig}s + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs); + } + + /** + * Interface for data enabled listener. + * + * @hide + */ + @SystemApi + public interface DataEnabledChangedListener { + /** + * Callback invoked when the data enabled changes. + * + * @param enabled {@code true} if data is enabled, otherwise disabled. + * @param reason Reason for data enabled/disabled. + * See {@link TelephonyManager.DataEnabledReason}. + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onDataEnabledChanged(boolean enabled, + @DataEnabledReason int reason); } /** @@ -707,6 +2004,7 @@ public class PhoneStateListener { * same as above, but with the network type. Both called. */ public void onDataConnectionStateChanged(int state, int networkType) { + // default implementation empty } /** @@ -754,6 +2052,7 @@ public class PhoneStateListener { * @param cellInfo is the list of currently visible cells. */ public void onCellInfoChanged(List<CellInfo> cellInfo) { + // default implementation empty } /** @@ -767,7 +2066,7 @@ public class PhoneStateListener { * @param callState {@link PreciseCallState} * @hide */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @SystemApi public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) { // default implementation empty @@ -786,9 +2085,9 @@ public class PhoneStateListener { * @param preciseDisconnectCause {@link PreciseDisconnectCause}. * */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) - public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause, - int preciseDisconnectCause) { + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause, + @PreciseDisconnectCauses int preciseDisconnectCause) { // default implementation empty } @@ -804,7 +2103,7 @@ public class PhoneStateListener { * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed. * */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) { // default implementation empty } @@ -826,7 +2125,7 @@ public class PhoneStateListener { * * @param dataConnectionState {@link PreciseDataConnectionState} */ - @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE)) + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged( @NonNull PreciseDataConnectionState dataConnectionState) { // default implementation empty @@ -864,6 +2163,7 @@ public class PhoneStateListener { */ @SystemApi public void onSrvccStateChanged(@SrvccState int srvccState) { + // default implementation empty } @@ -882,6 +2182,7 @@ public class PhoneStateListener { */ @SystemApi public void onVoiceActivationStateChanged(@SimActivationState int state) { + // default implementation empty } /** @@ -898,6 +2199,7 @@ public class PhoneStateListener { * @hide */ public void onDataActivationStateChanged(@SimActivationState int state) { + // default implementation empty } /** @@ -925,7 +2227,7 @@ public class PhoneStateListener { * * @param telephonyDisplayInfo The display information. */ - @RequiresPermission((android.Manifest.permission.READ_PHONE_STATE)) + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) { // default implementation empty } @@ -1036,7 +2338,8 @@ public class PhoneStateListener { /** * Callback invoked when OEM hook raw event is received on the registered subscription. * Note, the registration subId comes from {@link TelephonyManager} object which registers - * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}. + * PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. * If this TelephonyManager object was created with * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the * subId. Otherwise, this callback applies to @@ -1058,7 +2361,7 @@ public class PhoneStateListener { * @param capability the new phone capability * @hide */ - public void onPhoneCapabilityChanged(PhoneCapability capability) { + public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability) { // default implementation empty } @@ -1102,7 +2405,8 @@ public class PhoneStateListener { * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. * - * @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE} + * Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} + * * @param state the modem radio power state * @hide */ @@ -1178,18 +2482,6 @@ public class PhoneStateListener { } /** - * Callback invoked when the current physical channel configuration has changed - * - * @param configs List of the current {@link PhysicalChannelConfig}s - * @hide - */ - @SystemApi - public void onPhysicalChannelConfigurationChanged( - @NonNull List<PhysicalChannelConfig> configs) { - // default implementation empty - } - - /** * The callback methods need to be called on the handler thread where * this object was created. If the binder did that for us it'd be nice. * @@ -1471,18 +2763,16 @@ public class PhoneStateListener { Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onImsCallDisconnectCauseChanged(disconnectCause))); - } public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, - @NonNull String chosenPlmn, int domain, - int causeCode, int additionalCauseCode) { + @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onRegistrationFailed( - cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode))); + cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode))); // default implementation empty } @@ -1494,16 +2784,29 @@ public class PhoneStateListener { () -> mExecutor.execute(() -> psl.onBarringInfoChanged(barringInfo))); } - public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) { + public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); - if (psl == null) return; + PhysicalChannelConfigChangedListener listener = + (PhysicalChannelConfigChangedListener) mPhoneStateListenerWeakRef.get(); + if (listener == null) return; Binder.withCleanCallingIdentity( - () -> mExecutor.execute( - () -> psl.onPhysicalChannelConfigurationChanged(configs))); + () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged( + configs))); } - } + public void onDataEnabledChanged(boolean enabled, @DataEnabledReason int reason) { + if ((mPhoneStateListenerWeakRef.get() instanceof DataEnabledChangedListener)) { + DataEnabledChangedListener listener = + (DataEnabledChangedListener) mPhoneStateListenerWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onDataEnabledChanged( + enabled, reason))); + } + } + } private void log(String s) { Rlog.d(LOG_TAG, s); diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 24ed29a66654..cc3284a1f885 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -15,6 +15,7 @@ */ package android.telephony; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -24,6 +25,9 @@ import android.compat.annotation.EnabledAfter; import android.content.Context; import android.os.Binder; import android.os.Build; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.telephony.Annotation.CallState; @@ -37,6 +41,7 @@ import android.telephony.Annotation.SimActivationState; import android.telephony.Annotation.SrvccState; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; +import android.util.ArraySet; import android.util.Log; import com.android.internal.telephony.IOnSubscriptionsChangedListener; @@ -45,6 +50,7 @@ import com.android.internal.telephony.ITelephonyRegistry; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.Executor; /** @@ -206,7 +212,7 @@ public class TelephonyRegistryManager { } /** - * To check the SDK version for {@link #listenForSubscriber}. + * To check the SDK version for {@link #listenWithEventList}. */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P) @@ -218,23 +224,23 @@ public class TelephonyRegistryManager { * @param pkg Package name * @param featureId Feature ID * @param listener Listener providing callback - * @param events Events + * @param events List events * @param notifyNow Whether to notify instantly */ - public void listenForSubscriber(int subId, @NonNull String pkg, @NonNull String featureId, - @NonNull PhoneStateListener listener, long events, boolean notifyNow) { + public void listenWithEventList(int subId, @NonNull String pkg, @NonNull String featureId, + @NonNull PhoneStateListener listener, @NonNull int[] events, boolean notifyNow) { try { // subId from PhoneStateListener is deprecated Q on forward, use the subId from // TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q. if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) { // Since mSubId in PhoneStateListener is deprecated from Q on forward, this is // the only place to set mSubId and its for "informational" only. - listener.mSubId = (events == PhoneStateListener.LISTEN_NONE) + listener.mSubId = (events.length == 0) ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId; } else if (listener.mSubId != null) { subId = listener.mSubId; } - sRegistry.listenForSubscriber( + sRegistry.listenWithEventList( subId, pkg, featureId, listener.callback, events, notifyNow); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -771,13 +777,345 @@ public class TelephonyRegistryManager { * @param subId the subId * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel. */ - public void notifyPhysicalChannelConfigurationForSubscriber( + public void notifyPhysicalChannelConfigForSubscriber( int subId, List<PhysicalChannelConfig> configs) { try { - sRegistry.notifyPhysicalChannelConfigurationForSubscriber(subId, configs); + sRegistry.notifyPhysicalChannelConfigForSubscriber(subId, configs); } catch (RemoteException ex) { // system server crash } } + public @NonNull Set<Integer> getEventsFromListener(@NonNull PhoneStateListener listener) { + + Set<Integer> eventList = new ArraySet<>(); + + if (listener instanceof PhoneStateListener.ServiceStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.MessageWaitingIndicatorChangedListener) { + eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED); + } + + if (listener instanceof PhoneStateListener.CallForwardingIndicatorChangedListener) { + eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED); + } + + if (listener instanceof PhoneStateListener.CellLocationChangedListener) { + eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED); + } + + if (listener instanceof PhoneStateListener.CallStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.DataConnectionStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.DataActivityListener) { + eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED); + } + + if (listener instanceof PhoneStateListener.SignalStrengthsChangedListener) { + eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED); + } + + if (listener instanceof PhoneStateListener.AlwaysReportedSignalStrengthChangedListener) { + eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); + } + + if (listener instanceof PhoneStateListener.CellInfoChangedListener) { + eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED); + } + + if (listener instanceof PhoneStateListener.PreciseCallStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.CallDisconnectCauseChangedListener) { + eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); + } + + if (listener instanceof PhoneStateListener.ImsCallDisconnectCauseChangedListener) { + eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); + } + + if (listener instanceof PhoneStateListener.PreciseDataConnectionStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.SrvccStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.VoiceActivationStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.DataActivationStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.UserMobileDataStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.DisplayInfoChangedListener) { + eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED); + } + + if (listener instanceof PhoneStateListener.EmergencyNumberListChangedListener) { + eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED); + } + + if (listener instanceof PhoneStateListener.OutgoingEmergencyCallListener) { + eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL); + } + + if (listener instanceof PhoneStateListener.OutgoingEmergencySmsListener) { + eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); + } + + if (listener instanceof PhoneStateListener.PhoneCapabilityChangedListener) { + eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED); + } + + if (listener instanceof PhoneStateListener.ActiveDataSubscriptionIdChangedListener) { + eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); + } + + if (listener instanceof PhoneStateListener.RadioPowerStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.CarrierNetworkChangeListener) { + eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED); + } + + if (listener instanceof PhoneStateListener.RegistrationFailedListener) { + eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); + } + + if (listener instanceof PhoneStateListener.CallAttributesChangedListener) { + eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); + } + + if (listener instanceof PhoneStateListener.BarringInfoChangedListener) { + eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + } + + if (listener instanceof PhoneStateListener.PhysicalChannelConfigChangedListener) { + eventList.add(PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); + } + + if (listener instanceof PhoneStateListener.DataEnabledChangedListener) { + eventList.add(PhoneStateListener.EVENT_DATA_ENABLED_CHANGED); + } + + return eventList; + } + + private @NonNull Set<Integer> getEventsFromBitmask(int eventMask) { + + Set<Integer> eventList = new ArraySet<>(); + + if ((eventMask & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { + eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { + eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { + eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { + eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CALL_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { + eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { + eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { + eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) { + eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) { + eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) { + eventList.add(PhoneStateListener.EVENT_OEM_HOOK_RAW); + } + + if ((eventMask & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) { + eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) { + eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { + eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) { + eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) { + eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) { + eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) { + eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { + eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) { + eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) { + eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL) != 0) { + eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL); + } + + if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS) != 0) { + eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); + } + + if ((eventMask & PhoneStateListener.LISTEN_REGISTRATION_FAILURE) != 0) { + eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); + } + + if ((eventMask & PhoneStateListener.LISTEN_BARRING_INFO) != 0) { + eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + } + return eventList; + + } + + /** + * Registers a listener object to receive notification of changes + * in specified telephony states. + * <p> + * To register a listener, pass a {@link PhoneStateListener} which implements + * interfaces of events. For example, + * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements + * {@link PhoneStateListener.ServiceStateChangedListener}. + * + * At registration, and when a specified telephony state changes, the telephony manager invokes + * the appropriate callback method on the listener object and passes the current (updated) + * values. + * <p> + * + * If this TelephonyManager object has been created with + * {@link TelephonyManager#createForSubscriptionId}, applies to the given subId. + * Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}. + * To listen events for multiple subIds, pass a separate listener object to + * each TelephonyManager object created with {@link TelephonyManager#createForSubscriptionId}. + * + * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> + * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A + * {@link SecurityException} will be thrown otherwise. + * + * This API should be used sparingly -- large numbers of listeners will cause system + * instability. If a process has registered too many listeners without unregistering them, it + * may encounter an {@link IllegalStateException} when trying to register more listeners. + * + * @param listener The {@link PhoneStateListener} object to register. + */ + public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, int subId, + String pkgName, String attributionTag, @NonNull PhoneStateListener listener, + boolean notifyNow) { + listener.setExecutor(executor); + registerPhoneStateListener(subId, pkgName, attributionTag, listener, + getEventsFromListener(listener), notifyNow); + } + + public void registerPhoneStateListenerWithEvents(int subId, String pkgName, + String attributionTag, @NonNull PhoneStateListener listener, int events, + boolean notifyNow) { + registerPhoneStateListener( + subId, pkgName, attributionTag, listener, getEventsFromBitmask(events), notifyNow); + } + + private void registerPhoneStateListener(int subId, + String pkgName, String attributionTag, @NonNull PhoneStateListener listener, + @NonNull Set<Integer> events, boolean notifyNow) { + if (listener == null) { + throw new IllegalStateException("telephony service is null."); + } + + listenWithEventList(subId, pkgName, attributionTag, listener, + events.stream().mapToInt(i -> i).toArray(), notifyNow); + } + + /** + * Unregister an existing {@link PhoneStateListener}. + * + * @param listener The {@link PhoneStateListener} object to unregister. + */ + public void unregisterPhoneStateListener(int subId, String pkgName, String attributionTag, + @NonNull PhoneStateListener listener, + boolean notifyNow) { + listenWithEventList(subId, pkgName, attributionTag, listener, new int[0], notifyNow); + } } diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 84c209b2d063..00ff68736182 100755 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -698,7 +698,7 @@ public class DateFormat { } private static String zeroPad(int inValue, int inMinDigits) { - return TextUtils.formatSimple("%0" + inMinDigits + "d", inValue); + return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue); } /** diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java index cc6ed2e4539e..c8193b3b0dbb 100644 --- a/core/java/android/util/Xml.java +++ b/core/java/android/util/Xml.java @@ -18,6 +18,8 @@ package android.util; import android.annotation.NonNull; import android.annotation.Nullable; +import android.system.ErrnoException; +import android.system.Os; import com.android.internal.util.BinaryXmlPullParser; import com.android.internal.util.BinaryXmlSerializer; @@ -35,7 +37,7 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -43,6 +45,7 @@ import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; +import java.util.Arrays; /** * XML utility methods. @@ -59,6 +62,12 @@ public class Xml { public static String FEATURE_RELAXED = "http://xmlpull.org/v1/doc/features.html#relaxed"; /** + * Feature flag: when set, {@link #resolveSerializer(OutputStream)} will + * emit binary XML by default. + */ + private static final boolean ENABLE_BINARY_DEFAULT = false; + + /** * Parses the given xml string and fires events on the given SAX handler. */ public static void parse(String xml, ContentHandler contentHandler) @@ -119,6 +128,7 @@ public class Xml { * * @hide */ + @SuppressWarnings("AndroidFrameworkEfficientXml") public static @NonNull TypedXmlPullParser newFastPullParser() { return XmlUtils.makeTyped(newPullParser()); } @@ -151,8 +161,28 @@ public class Xml { */ public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in) throws IOException { - // TODO: add support for binary format - final TypedXmlPullParser xml = newFastPullParser(); + final byte[] magic = new byte[4]; + if (in instanceof FileInputStream) { + try { + Os.pread(((FileInputStream) in).getFD(), magic, 0, magic.length, 0); + } catch (ErrnoException e) { + throw e.rethrowAsIOException(); + } + } else { + if (!in.markSupported()) { + in = new BufferedInputStream(in); + } + in.mark(4); + in.read(magic); + in.reset(); + } + + final TypedXmlPullParser xml; + if (Arrays.equals(magic, BinaryXmlSerializer.PROTOCOL_MAGIC_VERSION_0)) { + xml = newBinaryPullParser(); + } else { + xml = newFastPullParser(); + } try { xml.setInput(in, StandardCharsets.UTF_8.name()); } catch (XmlPullParserException e) { @@ -177,6 +207,7 @@ public class Xml { * * @hide */ + @SuppressWarnings("AndroidFrameworkEfficientXml") public static @NonNull TypedXmlSerializer newFastSerializer() { return XmlUtils.makeTyped(new FastXmlSerializer()); } @@ -209,8 +240,12 @@ public class Xml { */ public static @NonNull TypedXmlSerializer resolveSerializer(@NonNull OutputStream out) throws IOException { - // TODO: add support for binary format - final TypedXmlSerializer xml = newFastSerializer(); + final TypedXmlSerializer xml; + if (ENABLE_BINARY_DEFAULT) { + xml = newBinarySerializer(); + } else { + xml = newFastSerializer(); + } xml.setOutput(out, StandardCharsets.UTF_8.name()); return xml; } diff --git a/core/java/android/uwb/AdapterStateListener.java b/core/java/android/uwb/AdapterStateListener.java new file mode 100644 index 000000000000..8875af385238 --- /dev/null +++ b/core/java/android/uwb/AdapterStateListener.java @@ -0,0 +1,149 @@ +/* + * 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.uwb; + +import android.annotation.NonNull; +import android.os.Binder; +import android.os.RemoteException; +import android.util.Log; +import android.uwb.UwbManager.AdapterStateCallback; +import android.uwb.UwbManager.AdapterStateCallback.StateChangedReason; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +/** + * @hide + */ +public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub { + private static final String TAG = "Uwb.StateListener"; + + private final IUwbAdapter mAdapter; + private boolean mIsRegistered = false; + + private final Map<AdapterStateCallback, Executor> mCallbackMap = new HashMap<>(); + + @StateChangedReason + private int mAdapterStateChangeReason = AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN; + private boolean mAdapterEnabledState = false; + + public AdapterStateListener(@NonNull IUwbAdapter adapter) { + mAdapter = adapter; + } + + /** + * Register an {@link AdapterStateCallback} with this {@link AdapterStateListener} + * + * @param executor an {@link Executor} to execute given callback + * @param callback user implementation of the {@link AdapterStateCallback} + */ + public void register(@NonNull Executor executor, @NonNull AdapterStateCallback callback) { + synchronized (this) { + if (mCallbackMap.containsKey(callback)) { + return; + } + + mCallbackMap.put(callback, executor); + + if (!mIsRegistered) { + try { + mAdapter.registerAdapterStateCallbacks(this); + mIsRegistered = true; + } catch (RemoteException e) { + Log.w(TAG, "Failed to register adapter state callback"); + executor.execute(() -> callback.onStateChanged(false, + AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN)); + } + } else { + sendCurrentState(callback); + } + } + } + + /** + * Unregister the specified {@link AdapterStateCallback} + * + * @param callback user implementation of the {@link AdapterStateCallback} + */ + public void unregister(@NonNull AdapterStateCallback callback) { + synchronized (this) { + if (!mCallbackMap.containsKey(callback)) { + return; + } + + mCallbackMap.remove(callback); + + if (mCallbackMap.isEmpty() && mIsRegistered) { + try { + mAdapter.unregisterAdapterStateCallbacks(this); + } catch (RemoteException e) { + Log.w(TAG, "Failed to unregister AdapterStateCallback with service"); + } + mIsRegistered = false; + } + } + } + + private void sendCurrentState(@NonNull AdapterStateCallback callback) { + synchronized (this) { + Executor executor = mCallbackMap.get(callback); + + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onStateChanged( + mAdapterEnabledState, mAdapterStateChangeReason)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + + @Override + public void onAdapterStateChanged(boolean isEnabled, int reason) { + synchronized (this) { + @StateChangedReason int localReason = + convertToStateChangedReason(reason); + mAdapterEnabledState = isEnabled; + mAdapterStateChangeReason = localReason; + for (AdapterStateCallback cb : mCallbackMap.keySet()) { + sendCurrentState(cb); + } + } + } + + private static @StateChangedReason int convertToStateChangedReason( + @StateChangeReason int reason) { + switch (reason) { + case StateChangeReason.ALL_SESSIONS_CLOSED: + return AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED; + + case StateChangeReason.SESSION_STARTED: + return AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED; + + case StateChangeReason.SYSTEM_POLICY: + return AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY; + + case StateChangeReason.SYSTEM_BOOT: + return AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT; + + case StateChangeReason.UNKNOWN: + default: + return AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN; + } + } +} diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl index d29ed34804f1..2c8b2e462510 100644 --- a/core/java/android/uwb/IUwbAdapter.aidl +++ b/core/java/android/uwb/IUwbAdapter.aidl @@ -98,11 +98,18 @@ interface IUwbAdapter { int getMaxSimultaneousSessions(); /** - * Get the maximum number of remote devices per session + * Get the maximum number of remote devices per session when local device is initiator * * @return the maximum number of remote devices supported in a single session */ - int getMaxRemoteDevicesPerSession(); + int getMaxRemoteDevicesPerInitiatorSession(); + + /** + * Get the maximum number of remote devices per session when local device is responder + * + * @return the maximum number of remote devices supported in a single session + */ + int getMaxRemoteDevicesPerResponderSession(); /** * Provides the capabilities and features of the device diff --git a/core/java/android/uwb/StateChangeReason.aidl b/core/java/android/uwb/StateChangeReason.aidl index 46a6e2edfa22..28eaf9f2b24e 100644 --- a/core/java/android/uwb/StateChangeReason.aidl +++ b/core/java/android/uwb/StateChangeReason.aidl @@ -41,5 +41,10 @@ enum StateChangeReason { * The adapter state changed because of a device system change. */ SYSTEM_POLICY, + + /** + * Used to signal the first adapter state message after boot + */ + SYSTEM_BOOT, } diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index 2f1e2ded26ac..ed5cf3625525 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -16,13 +16,21 @@ package android.uwb; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SuppressLint; +import android.annotation.SystemService; +import android.content.Context; +import android.os.IBinder; import android.os.PersistableBundle; +import android.os.RemoteException; +import android.os.ServiceManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; @@ -36,7 +44,13 @@ import java.util.concurrent.Executor; * * @hide */ +@SystemService(Context.UWB_SERVICE) public final class UwbManager { + private IUwbAdapter mUwbAdapter; + private static final String SERVICE_NAME = "uwb"; + + private AdapterStateListener mAdapterStateListener; + /** * Interface for receiving UWB adapter state changes */ @@ -96,10 +110,31 @@ public final class UwbManager { /** * Use <code>Context.getSystemService(UwbManager.class)</code> to get an instance. + * + * @param adapter an instance of an {@link android.uwb.IUwbAdapter} */ - private UwbManager() { - throw new UnsupportedOperationException(); + private UwbManager(IUwbAdapter adapter) { + mUwbAdapter = adapter; + mAdapterStateListener = new AdapterStateListener(adapter); } + + /** + * @hide + */ + public static UwbManager getInstance() { + IBinder b = ServiceManager.getService(SERVICE_NAME); + if (b == null) { + return null; + } + + IUwbAdapter adapter = IUwbAdapter.Stub.asInterface(b); + if (adapter == null) { + return null; + } + + return new UwbManager(adapter); + } + /** * Register an {@link AdapterStateCallback} to listen for UWB adapter state changes * <p>The provided callback will be invoked by the given {@link Executor}. @@ -112,8 +147,9 @@ public final class UwbManager { * @param executor an {@link Executor} to execute given callback * @param callback user implementation of the {@link AdapterStateCallback} */ - public void registerAdapterStateCallback(Executor executor, AdapterStateCallback callback) { - throw new UnsupportedOperationException(); + public void registerAdapterStateCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull AdapterStateCallback callback) { + mAdapterStateListener.register(executor, callback); } /** @@ -125,8 +161,8 @@ public final class UwbManager { * * @param callback user implementation of the {@link AdapterStateCallback} */ - public void unregisterAdapterStateCallback(AdapterStateCallback callback) { - throw new UnsupportedOperationException(); + public void unregisterAdapterStateCallback(@NonNull AdapterStateCallback callback) { + mAdapterStateListener.unregister(callback); } /** @@ -139,7 +175,11 @@ public final class UwbManager { */ @NonNull public PersistableBundle getSpecificationInfo() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.getSpecificationInfo(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -148,7 +188,11 @@ public final class UwbManager { * @return true if ranging is supported */ public boolean isRangingSupported() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.isRangingSupported(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } @Retention(RetentionPolicy.SOURCE) @@ -197,7 +241,24 @@ public final class UwbManager { */ @AngleOfArrivalSupportType public int getAngleOfArrivalSupport() { - throw new UnsupportedOperationException(); + try { + switch (mUwbAdapter.getAngleOfArrivalSupport()) { + case AngleOfArrivalSupport.TWO_DIMENSIONAL: + return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D; + + case AngleOfArrivalSupport.THREE_DIMENSIONAL_HEMISPHERICAL: + return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL; + + case AngleOfArrivalSupport.THREE_DIMENSIONAL_SPHERICAL: + return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL; + + case AngleOfArrivalSupport.NONE: + default: + return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -211,7 +272,15 @@ public final class UwbManager { */ @NonNull public List<Integer> getSupportedChannelNumbers() { - throw new UnsupportedOperationException(); + List<Integer> channels = new ArrayList<>(); + try { + for (int channel : mUwbAdapter.getSupportedChannels()) { + channels.add(channel); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return channels; } /** @@ -222,7 +291,15 @@ public final class UwbManager { */ @NonNull public Set<Integer> getSupportedPreambleCodeIndices() { - throw new UnsupportedOperationException(); + Set<Integer> preambles = new HashSet<>(); + try { + for (int preamble : mUwbAdapter.getSupportedPreambleCodes()) { + preambles.add(preamble); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return preambles; } /** @@ -234,7 +311,11 @@ public final class UwbManager { */ @SuppressLint("MethodNameUnits") public long elapsedRealtimeResolutionNanos() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.getTimestampResolutionNanos(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -243,7 +324,11 @@ public final class UwbManager { * @return the maximum allowed number of simultaneously open {@link RangingSession} instances. */ public int getMaxSimultaneousSessions() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.getMaxSimultaneousSessions(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -253,7 +338,11 @@ public final class UwbManager { * @return the maximum number of remote devices per {@link RangingSession} */ public int getMaxRemoteDevicesPerInitiatorSession() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.getMaxRemoteDevicesPerInitiatorSession(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -263,7 +352,11 @@ public final class UwbManager { * @return the maximum number of remote devices per {@link RangingSession} */ public int getMaxRemoteDevicesPerResponderSession() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.getMaxRemoteDevicesPerResponderSession(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java index b58937beed55..fcf699faafe1 100644 --- a/core/java/android/view/ContentInfo.java +++ b/core/java/android/view/ContentInfo.java @@ -20,16 +20,16 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ClipData; +import android.content.ClipDescription; import android.net.Uri; import android.os.Bundle; -import android.util.ArrayMap; +import android.util.Pair; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Map; import java.util.Objects; import java.util.function.Predicate; @@ -208,50 +208,51 @@ public final class ContentInfo { } /** - * Partitions the content based on the given predicate. + * Partitions this content based on the given predicate. * - * <p>Similar to a - * {@link java.util.stream.Collectors#partitioningBy(Predicate) partitioning collector}, - * this function classifies the content and organizes it into a map, grouping the items that - * matched vs didn't match the predicate. + * <p>This function classifies the content and organizes it into a pair, grouping the items + * that matched vs didn't match the predicate. * * <p>Except for the {@link ClipData} items, the returned objects will contain all the same - * metadata as the original. + * metadata as this {@link ContentInfo}. * * @param itemPredicate The predicate to test each {@link ClipData.Item} to determine which - * partition to place it into. - * @return A map containing the partitioned content. The map will contain a single entry if - * all items were classified into the same partition (all matched or all didn't match the - * predicate) or two entries (if there's at least one item that matched the predicate and at - * least one item that didn't match the predicate). + * partition to place it into. + * @return A pair containing the partitioned content. The pair's first object will have the + * content that matched the predicate, or null if none of the items matched. The pair's + * second object will have the content that didn't match the predicate, or null if all of + * the items matched. */ @NonNull - public Map<Boolean, ContentInfo> partition(@NonNull Predicate<ClipData.Item> itemPredicate) { + public Pair<ContentInfo, ContentInfo> partition( + @NonNull Predicate<ClipData.Item> itemPredicate) { if (mClip.getItemCount() == 1) { - Map<Boolean, ContentInfo> result = new ArrayMap<>(1); - result.put(itemPredicate.test(mClip.getItemAt(0)), this); - return result; + boolean matched = itemPredicate.test(mClip.getItemAt(0)); + return Pair.create(matched ? this : null, matched ? null : this); } - ArrayList<ClipData.Item> accepted = new ArrayList<>(); - ArrayList<ClipData.Item> remaining = new ArrayList<>(); + ArrayList<ClipData.Item> acceptedItems = new ArrayList<>(); + ArrayList<ClipData.Item> remainingItems = new ArrayList<>(); for (int i = 0; i < mClip.getItemCount(); i++) { ClipData.Item item = mClip.getItemAt(i); if (itemPredicate.test(item)) { - accepted.add(item); + acceptedItems.add(item); } else { - remaining.add(item); + remainingItems.add(item); } } - Map<Boolean, ContentInfo> result = new ArrayMap<>(2); - if (!accepted.isEmpty()) { - ClipData acceptedClip = new ClipData(mClip.getDescription(), accepted); - result.put(true, new Builder(this).setClip(acceptedClip).build()); + if (acceptedItems.isEmpty()) { + return Pair.create(null, this); } - if (!remaining.isEmpty()) { - ClipData remainingClip = new ClipData(mClip.getDescription(), remaining); - result.put(false, new Builder(this).setClip(remainingClip).build()); + if (remainingItems.isEmpty()) { + return Pair.create(this, null); } - return result; + ContentInfo accepted = new Builder(this) + .setClip(new ClipData(new ClipDescription(mClip.getDescription()), acceptedItems)) + .build(); + ContentInfo remaining = new Builder(this) + .setClip(new ClipData(new ClipDescription(mClip.getDescription()), remainingItems)) + .build(); + return Pair.create(accepted, remaining); } /** diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 56c7e27151e0..9991367e6bfd 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -1512,6 +1512,9 @@ public final class Display { * then 1920x1080 50Hz will have alternative refresh rate of 60Hz. If 1920x1080 60Hz * has an alternative of 50Hz and 1920x1080 50Hz has an alternative of 24Hz, then 1920x1080 * 60Hz will also have an alternative of 24Hz. + * + * @see Surface#setFrameRate + * @see SurfaceControl.Transaction#setFrameRate */ @NonNull public float[] getAlternativeRefreshRates() { diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl deleted file mode 100644 index 1cf83a3f1e79..000000000000 --- a/core/java/android/view/IPinnedStackController.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 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 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.view; - -import android.graphics.Rect; - -/** - * An interface to the PinnedStackController to update it of state changes, and to query - * information based on the current state. - * - * @hide - */ -interface IPinnedStackController { - /** - * @return what WM considers to be the current device rotation. - */ - int getDisplayRotation(); -} diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl index f49fee36ca7e..84dd8af5e342 100644 --- a/core/java/android/view/IPinnedStackListener.aidl +++ b/core/java/android/view/IPinnedStackListener.aidl @@ -21,7 +21,6 @@ import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.graphics.Rect; import android.view.DisplayInfo; -import android.view.IPinnedStackController; /** * Listener for changes to the pinned stack made by the WindowManager. @@ -31,12 +30,6 @@ import android.view.IPinnedStackController; oneway interface IPinnedStackListener { /** - * Called when the listener is registered and provides an interface to call back to the pinned - * stack controller to update the controller of the pinned stack state. - */ - void onListenerRegistered(IPinnedStackController controller); - - /** * Called when the window manager has detected a change that would cause the movement bounds * to be changed (ie. after configuration change, aspect ratio change, etc). */ diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index a23b7e193024..025a977d5420 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -780,4 +780,25 @@ interface IWindowManager * verified. */ boolean verifyImpressionToken(in ImpressionToken impressionToken); + + /** + * Registers a listener for a {@link android.app.WindowContext} to handle configuration changes + * from the server side. + * + * @param clientToken the window context's token + * @param type Window type of the window context + * @param displayId The display associated with the window context + * @param options A bundle used to pass window-related options and choose the right DisplayArea + * + * @return {@code true} if the listener was registered successfully. + */ + boolean registerWindowContextListener(IBinder clientToken, int type, int displayId, + in Bundle options); + + /** + * Unregisters a listener which registered with {@link #registerWindowContextListener()}. + * + * @param clientToken the window context's token + */ + void unregisterWindowContextListener(IBinder clientToken); } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index c4f32c433598..80437be1dcd4 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1057,6 +1057,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean canRun = false; if (show) { // Show request + if (fromIme) { + ImeTracing.getInstance().triggerClientDump( + "ImeInsetsSourceConsumer#requestShow", mHost.getInputMethodManager(), + null /* icProto */); + } switch(consumer.requestShow(fromIme)) { case ShowResult.SHOW_IMMEDIATELY: canRun = true; @@ -1096,8 +1101,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation + fromIme); // We don't have a control at the moment. However, we still want to update requested // visibility state such that in case we get control, we can apply show animation. + if (fromIme) { + ImeTracing.getInstance().triggerClientDump( + "InsetsSourceConsumer#show", mHost.getInputMethodManager(), + null /* icProto */); + } consumer.show(fromIme); } else if (animationType == ANIMATION_TYPE_HIDE) { + if (fromIme) { + ImeTracing.getInstance().triggerClientDump( + "InsetsSourceConsumer#hide", mHost.getInputMethodManager(), + null /* icProto */); + } consumer.hide(); } } @@ -1217,8 +1232,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void applyLocalVisibilityOverride() { for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { - final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i); - controller.applyLocalVisibilityOverride(); + final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); + consumer.applyLocalVisibilityOverride(); } } diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 537fd42d7135..fd1c3b82ca2c 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -25,6 +25,7 @@ import static android.view.InsetsSourceConsumerProto.IS_REQUESTED_VISIBLE; import static android.view.InsetsSourceConsumerProto.PENDING_FRAME; import static android.view.InsetsSourceConsumerProto.PENDING_VISIBLE_FRAME; import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL; +import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.getDefaultVisibility; import static android.view.InsetsState.toPublicType; @@ -34,6 +35,7 @@ import android.annotation.IntDef; import android.annotation.Nullable; import android.graphics.Rect; import android.util.Log; +import android.util.imetracing.ImeTracing; import android.util.proto.ProtoOutputStream; import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; @@ -108,6 +110,10 @@ public class InsetsSourceConsumer { */ public void setControl(@Nullable InsetsSourceControl control, @InsetsType int[] showTypes, @InsetsType int[] hideTypes) { + if (mType == ITYPE_IME) { + ImeTracing.getInstance().triggerClientDump("InsetsSourceConsumer#setControl", + mController.getHost().getInputMethodManager(), null /* icProto */); + } if (mSourceControl == control) { return; } @@ -237,6 +243,12 @@ public class InsetsSourceConsumer { final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType); final boolean hasControl = mSourceControl != null; + if (mType == ITYPE_IME) { + ImeTracing.getInstance().triggerClientDump( + "InsetsSourceConsumer#applyLocalVisibilityOverride", + mController.getHost().getInputMethodManager(), null /* icProto */); + } + // We still need to let the legacy app know the visibility change even if we don't have the // control. If we don't have the source, we don't change the requested visibility for making // the callback behavior compatible. diff --git a/core/java/android/view/OnReceiveContentListener.java b/core/java/android/view/OnReceiveContentListener.java index b551fa80b5cd..419f964ccd25 100644 --- a/core/java/android/view/OnReceiveContentListener.java +++ b/core/java/android/view/OnReceiveContentListener.java @@ -35,12 +35,12 @@ import android.annotation.Nullable; * * @Override * public ContentInfo onReceiveContent(View view, ContentInfo payload) { - * Map<Boolean, ContentInfo> split = + * Pair<ContentInfo, ContentInfo> split = * payload.partition(item -> item.getUri() != null); - * ContentInfo uriItems = split.get(true); - * ContentInfo remainingItems = split.get(false); - * if (uriItems != null) { - * ClipData clip = uriItems.getClip(); + * ContentInfo uriContent = split.first; + * ContentInfo remaining = split.second; + * if (uriContent != null) { + * ClipData clip = uriContent.getClip(); * for (int i = 0; i < clip.getItemCount(); i++) { * Uri uri = clip.getItemAt(i).getUri(); * // ... app-specific logic to handle the URI ... @@ -48,7 +48,7 @@ import android.annotation.Nullable; * } * // Return anything that we didn't handle ourselves. This preserves the default platform * // behavior for text and anything else for which we are not implementing custom handling. - * return remainingItems; + * return remaining; * } * } * diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 57ca71ae4b02..d839e3532b64 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.FrameInfo; import android.graphics.HardwareRenderer; import android.graphics.Picture; import android.graphics.Point; @@ -610,8 +611,7 @@ public final class ThreadedRenderer extends HardwareRenderer { * @param attachInfo AttachInfo tied to the specified view. */ void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) { - final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer; - choreographer.mFrameInfo.markDrawStart(); + attachInfo.mViewRootImpl.mViewFrameInfo.markDrawStart(); updateRootDisplayList(view, callbacks); @@ -629,7 +629,9 @@ public final class ThreadedRenderer extends HardwareRenderer { attachInfo.mPendingAnimatingRenderNodes = null; } - int syncResult = syncAndDrawFrame(choreographer.mFrameInfo); + final FrameInfo frameInfo = attachInfo.mViewRootImpl.getUpdatedFrameInfo(); + + int syncResult = syncAndDrawFrame(frameInfo); if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { Log.w("OpenGLRenderer", "Surface lost, forcing relayout"); // We lost our surface. For a relayout next frame which should give us a new diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a5c66537b11f..29cc4b507acc 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -27953,10 +27953,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Requests pointer capture mode. * <p> * When the window has pointer capture, the mouse pointer icon will disappear and will not - * change its position. Further mouse will be dispatched with the source - * {@link InputDevice#SOURCE_MOUSE_RELATIVE}, and relative position changes will be available - * through {@link MotionEvent#getX} and {@link MotionEvent#getY}. Non-mouse events - * (touchscreens, or stylus) will not be affected. + * change its position. Enabling pointer capture will change the behavior of input devices in + * the following ways: + * <ul> + * <li>Events from a mouse will be delivered with the source + * {@link InputDevice#SOURCE_MOUSE_RELATIVE}, and relative position changes will be + * available through {@link MotionEvent#getX} and {@link MotionEvent#getY}.</li> + * + * <li>Events from a touchpad will be delivered with the source + * {@link InputDevice#SOURCE_TOUCHPAD}, where the absolute position of each of the pointers + * on the touchpad will be available through {@link MotionEvent#getX(int)} and + * {@link MotionEvent#getY(int)}, and their relative movements are stored in + * {@link MotionEvent#AXIS_RELATIVE_X} and {@link MotionEvent#AXIS_RELATIVE_Y}.</li> + * + * <li>Events from other types of devices, such as touchscreens, will not be affected.</li> + * </ul> + * <p> + * Events captured through pointer capture will be dispatched to + * {@link OnCapturedPointerListener#onCapturedPointer(View, MotionEvent)} if an + * {@link OnCapturedPointerListener} is set, and otherwise to + * {@link #onCapturedPointerEvent(MotionEvent)}. * <p> * If the window already has pointer capture, this call does nothing. * <p> @@ -27965,6 +27981,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #releasePointerCapture() * @see #hasPointerCapture() + * @see #onPointerCaptureChange(boolean) */ public void requestPointerCapture() { final ViewRootImpl viewRootImpl = getViewRootImpl(); @@ -27980,6 +27997,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * If the window does not have pointer capture, this call will do nothing. * @see #requestPointerCapture() * @see #hasPointerCapture() + * @see #onPointerCaptureChange(boolean) */ public void releasePointerCapture() { final ViewRootImpl viewRootImpl = getViewRootImpl(); diff --git a/core/java/android/view/ViewFrameInfo.java b/core/java/android/view/ViewFrameInfo.java new file mode 100644 index 000000000000..890d071f8090 --- /dev/null +++ b/core/java/android/view/ViewFrameInfo.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.FrameInfo; + +/** + * The timing information of events taking place in ViewRootImpl + * @hide + */ +public class ViewFrameInfo { + public long drawStart; + public long oldestInputEventTime; // the time of the oldest input event consumed for this frame + public long newestInputEventTime; // the time of the newest input event consumed for this frame + // Various flags set to provide extra metadata about the current frame. See flag definitions + // inside FrameInfo. + // @see android.graphics.FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED + public long flags; + + /** + * Update the oldest event time. + * @param eventTime the time of the input event + */ + public void updateOldestInputEvent(long eventTime) { + if (oldestInputEventTime == 0 || eventTime < oldestInputEventTime) { + oldestInputEventTime = eventTime; + } + } + + /** + * Update the newest event time. + * @param eventTime the time of the input event + */ + public void updateNewestInputEvent(long eventTime) { + if (newestInputEventTime == 0 || eventTime > newestInputEventTime) { + newestInputEventTime = eventTime; + } + } + + /** + * Populate the missing fields using the data from ViewFrameInfo + * @param frameInfo : the structure FrameInfo object to populate + */ + public void populateFrameInfo(FrameInfo frameInfo) { + frameInfo.frameInfo[FrameInfo.FLAGS] |= flags; + frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart; + frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT] = oldestInputEventTime; + frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT] = newestInputEventTime; + } + + /** + * Reset this data. Should typically be invoked after calling "populateFrameInfo". + */ + public void reset() { + drawStart = 0; + oldestInputEventTime = 0; + newestInputEventTime = 0; + flags = 0; + } + + /** + * Record the current time, and store it in 'drawStart' + */ + public void markDrawStart() { + drawStart = System.nanoTime(); + } +} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8321f2b28771..2d633cbeb353 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -19,6 +19,7 @@ package android.view; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.InputDevice.SOURCE_CLASS_NONE; +import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.SIZE; @@ -118,6 +119,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.graphics.RenderNode; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.input.InputManager; @@ -423,7 +425,7 @@ public final class ViewRootImpl implements ViewParent, @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) int mHeight; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - Rect mDirty; + private Rect mDirty; public boolean mIsAnimating; private boolean mUseMTRenderer; @@ -446,6 +448,23 @@ public final class ViewRootImpl implements ViewParent, @UnsupportedAppUsage FallbackEventHandler mFallbackEventHandler; final Choreographer mChoreographer; + protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo(); + + /** + * Update the Choreographer's FrameInfo object with the timing information for the current + * ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next + * frame. + * @return the updated FrameInfo object + */ + protected @NonNull FrameInfo getUpdatedFrameInfo() { + // Since Choreographer is a thread-local singleton while we can have multiple + // ViewRootImpl's, populate the frame information from the current viewRootImpl before + // starting the draw + FrameInfo frameInfo = mChoreographer.mFrameInfo; + mViewFrameInfo.populateFrameInfo(frameInfo); + mViewFrameInfo.reset(); + return frameInfo; + } // used in relayout to get SurfaceControl size // for BLAST adapter surface setup @@ -2675,7 +2694,7 @@ public final class ViewRootImpl implements ViewParent, // to resume them mDirty.set(0, 0, mWidth, mHeight); } - mChoreographer.mFrameInfo.addFlags(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED); + mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED; } relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); @@ -4396,6 +4415,14 @@ public final class ViewRootImpl implements ViewParent, mView.mContext.getDrawable(value.resourceId); } } + // Sets the focus appearance data into the accessibility focus drawable. + if (mAttachInfo.mAccessibilityFocusDrawable instanceof GradientDrawable) { + final GradientDrawable drawable = + (GradientDrawable) mAttachInfo.mAccessibilityFocusDrawable; + drawable.setStroke(mAccessibilityManager.getAccessibilityFocusStrokeWidth(), + mAccessibilityManager.getAccessibilityFocusColor()); + } + return mAttachInfo.mAccessibilityFocusDrawable; } @@ -7914,6 +7941,10 @@ public final class ViewRootImpl implements ViewParent, if (mTranslator != null) { mTranslator.translateInsetsStateInScreenToAppWindow(insetsState); } + if (insetsState != null && insetsState.getSource(ITYPE_IME).isVisible()) { + ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsChanged", + getInsetsController().getHost().getInputMethodManager(), null /* icProto */); + } mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget(); } @@ -7930,6 +7961,10 @@ public final class ViewRootImpl implements ViewParent, if (mTranslator != null) { mTranslator.translateInsetsStateInScreenToAppWindow(insetsState); } + if (insetsState != null && insetsState.getSource(ITYPE_IME).isVisible()) { + ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsControlChanged", + getInsetsController().getHost().getInputMethodManager(), null /* icProto */); + } SomeArgs args = SomeArgs.obtain(); args.arg1 = insetsState; args.arg2 = activeControls; @@ -8138,7 +8173,8 @@ public final class ViewRootImpl implements ViewParent, oldestEventTime = me.getHistoricalEventTimeNano(0); } } - mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime); + mViewFrameInfo.updateOldestInputEvent(oldestEventTime); + mViewFrameInfo.updateNewestInputEvent(eventTime); deliverInputEvent(q); } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 9c163783124e..13d9eb9d3c7d 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -45,7 +45,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.os.RemoteException; import android.transition.Scene; import android.transition.Transition; import android.transition.TransitionManager; @@ -635,7 +634,7 @@ public abstract class Window { * Moves the activity between {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing * mode and {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. */ - void toggleFreeformWindowingMode() throws RemoteException; + void toggleFreeformWindowingMode(); /** * Puts the activity in picture-in-picture mode if the activity supports. diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index a8534faf43e5..56dcd5951e5e 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -19,9 +19,11 @@ package android.view.accessibility; import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME; import android.Manifest; +import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType; import android.accessibilityservice.AccessibilityShortcutInfo; +import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -59,6 +61,7 @@ import android.view.IWindow; import android.view.View; import android.view.accessibility.AccessibilityEvent.EventType; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IntPair; @@ -233,6 +236,11 @@ public final class AccessibilityManager { private int mPerformingAction = 0; + /** The stroke width of the focus rectangle in pixels */ + private int mFocusStrokeWidth; + /** The color of the focus rectangle */ + private int mFocusColor; + @UnsupportedAppUsage private final ArrayMap<AccessibilityStateChangeListener, Handler> mAccessibilityStateChangeListeners = new ArrayMap<>(); @@ -410,6 +418,13 @@ public final class AccessibilityManager { public void setRelevantEventTypes(int eventTypes) { mRelevantEventTypes = eventTypes; } + + @Override + public void setFocusAppearance(int strokeWidth, int color) { + synchronized (mLock) { + updateFocusAppearanceLocked(strokeWidth, color); + } + } }; /** @@ -457,6 +472,7 @@ public final class AccessibilityManager { mHandler = new Handler(context.getMainLooper(), mCallback); mUserId = userId; synchronized (mLock) { + initialFocusAppearanceLocked(context.getResources()); tryConnectToServiceLocked(service); } } @@ -464,18 +480,26 @@ public final class AccessibilityManager { /** * Create an instance. * + * @param context A {@link Context}. * @param handler The handler to use * @param service An interface to the backing service. * @param userId User id under which to run. + * @param serviceConnect {@code true} to connect the service or + * {@code false} not to connect the service. * * @hide */ - public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) { + @VisibleForTesting + public AccessibilityManager(Context context, Handler handler, IAccessibilityManager service, + int userId, boolean serviceConnect) { mCallback = new MyCallback(); mHandler = handler; mUserId = userId; synchronized (mLock) { - tryConnectToServiceLocked(service); + initialFocusAppearanceLocked(context.getResources()); + if (serviceConnect) { + tryConnectToServiceLocked(service); + } } } @@ -954,6 +978,32 @@ public final class AccessibilityManager { } /** + * Gets the strokeWidth of the focus rectangle. This value can be set by + * {@link AccessibilityService}. + * + * @return The strokeWidth of the focus rectangle in pixels. + * + */ + public int getAccessibilityFocusStrokeWidth() { + synchronized (mLock) { + return mFocusStrokeWidth; + } + } + + /** + * Gets the color of the focus rectangle. This value can be set by + * {@link AccessibilityService}. + * + * @return The color of the focus rectangle. + * + */ + public @ColorInt int getAccessibilityFocusColor() { + synchronized (mLock) { + return mFocusColor; + } + } + + /** * Get the preparers that are registered for an accessibility ID * * @param id The ID of interest @@ -1551,6 +1601,7 @@ public final class AccessibilityManager { setStateLocked(IntPair.first(userStateAndRelevantEvents)); mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents); updateUiTimeout(service.getRecommendedTimeoutMillis()); + updateFocusAppearanceLocked(service.getFocusStrokeWidth(), service.getFocusColor()); mService = service; } catch (RemoteException re) { Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); @@ -1635,6 +1686,40 @@ public final class AccessibilityManager { } /** + * Updates the stroke width and color of the focus rectangle. + * + * @param strokeWidth The strokeWidth of the focus rectangle. + * @param color The color of the focus rectangle. + */ + private void updateFocusAppearanceLocked(int strokeWidth, int color) { + if (mFocusStrokeWidth == strokeWidth && mFocusColor == color) { + return; + } + mFocusStrokeWidth = strokeWidth; + mFocusColor = color; + } + + /** + * Sets the stroke width and color of the focus rectangle to default value. + * + * @param resource The resources. + */ + private void initialFocusAppearanceLocked(Resources resource) { + try { + mFocusStrokeWidth = resource.getDimensionPixelSize( + R.dimen.accessibility_focus_highlight_stroke_width); + mFocusColor = resource.getColor(R.color.accessibility_focus_highlight_color); + } catch (Resources.NotFoundException re) { + // Sets the stroke width and color to default value by hardcoded for making + // the Talkback can work normally. + mFocusStrokeWidth = (int) (4 * resource.getDisplayMetrics().density); + mFocusColor = 0xbf39b500; + Log.e(LOG_TAG, "Error while initialing the focus appearance data then setting to" + + " default value by hardcoded", re); + } + } + + /** * Determines if the accessibility button within the system navigation area is supported. * * @return {@code true} if the accessibility button is supported on this device, diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 5d3c72015ee3..c71ea53c414d 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -92,4 +92,8 @@ interface IAccessibilityManager { void associateEmbeddedHierarchy(IBinder host, IBinder embedded); void disassociateEmbeddedHierarchy(IBinder token); + + int getFocusStrokeWidth(); + + int getFocusColor(); } diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl index 94b9ad1c3279..041399ccb8ec 100644 --- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl @@ -29,4 +29,6 @@ oneway interface IAccessibilityManagerClient { void notifyServicesStateChanged(long updatedUiTimeout); void setRelevantEventTypes(int eventTypes); + + void setFocusAppearance(int strokeWidth, int color); } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 907b5b085b59..eaf72dce62fe 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -598,6 +598,9 @@ public final class InputMethodManager { */ @Override public void finishInput() { + ImeTracing.getInstance().triggerClientDump( + "InputMethodManager.DelegateImpl#finishInput", InputMethodManager.this, + null /* icProto */); synchronized (mH) { finishInputLocked(); } @@ -641,6 +644,10 @@ public final class InputMethodManager { int startInputFlags = getStartInputFlags(focusedView, 0); startInputFlags |= StartInputFlags.WINDOW_GAINED_FOCUS; + ImeTracing.getInstance().triggerClientDump( + "InputMethodManager.DelegateImpl#startInputAsyncOnWindowFocusGain", + InputMethodManager.this, null /* icProto */); + final ImeFocusController controller = getFocusController(); if (controller == null) { return; @@ -950,6 +957,9 @@ public final class InputMethodManager { case MSG_APPLY_IME_VISIBILITY: { synchronized (mH) { if (mImeInsetsConsumer != null) { + ImeTracing.getInstance().triggerClientDump( + "ImeInsetsSourceConsumer#applyImeVisibility", + InputMethodManager.this, null /* icProto */); mImeInsetsConsumer.applyImeVisibility(msg.arg1 != 0); } } @@ -1860,6 +1870,9 @@ public final class InputMethodManager { * {@link #HIDE_NOT_ALWAYS} bit set. **/ public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) { + ImeTracing.getInstance().triggerClientDump( + "InputMethodManager#toggleSoftInputFromWindow", InputMethodManager.this, + null /* icProto */); synchronized (mH) { final View servedView = getServedViewLocked(); if (servedView == null || servedView.getWindowToken() != windowToken) { @@ -1887,6 +1900,9 @@ public final class InputMethodManager { * {@link #HIDE_NOT_ALWAYS} bit set. */ public void toggleSoftInput(int showFlags, int hideFlags) { + ImeTracing.getInstance().triggerClientDump( + "InputMethodManager#toggleSoftInput", InputMethodManager.this, + null /* icProto */); if (mCurMethod != null) { try { mCurMethod.toggleSoftInput(showFlags, hideFlags); diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index d8a632d10bc3..5b32649ac55b 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -45,15 +45,19 @@ public final class TextSelection implements Parcelable { private final int mEndIndex; private final EntityConfidence mEntityConfidence; @Nullable private final String mId; + @Nullable + private final TextClassification mTextClassification; private final Bundle mExtras; private TextSelection( int startIndex, int endIndex, Map<String, Float> entityConfidence, String id, + @Nullable TextClassification textClassification, Bundle extras) { mStartIndex = startIndex; mEndIndex = endIndex; mEntityConfidence = new EntityConfidence(entityConfidence); mId = id; + mTextClassification = textClassification; mExtras = extras; } @@ -111,6 +115,19 @@ public final class TextSelection implements Parcelable { } /** + * Returns the text classification result of the suggested selection span. Enables the text + * classification by calling + * {@link TextSelection.Request.Builder#setIncludeTextClassification(boolean)}. If the text + * classifier does not support it, a {@code null} is returned. + * + * @see TextSelection.Request.Builder#setIncludeTextClassification(boolean) + */ + @Nullable + public TextClassification getTextClassification() { + return mTextClassification; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -138,6 +155,8 @@ public final class TextSelection implements Parcelable { private final Map<String, Float> mEntityConfidence = new ArrayMap<>(); @Nullable private String mId; @Nullable + private TextClassification mTextClassification; + @Nullable private Bundle mExtras; /** @@ -179,6 +198,21 @@ public final class TextSelection implements Parcelable { } /** + * Sets the text classification result of the suggested selection. If + * {@link Request#shouldIncludeTextClassification()} is {@code true}, set this value. + * Otherwise this value may be set to null. The intention of this method is to avoid + * doing expensive work if the client is not interested in such result. + * + * @return this builder + * @see Request#shouldIncludeTextClassification() + */ + @NonNull + public Builder setTextClassification(@Nullable TextClassification textClassification) { + mTextClassification = textClassification; + return this; + } + + /** * Sets the extended data. * * @return this builder @@ -196,7 +230,7 @@ public final class TextSelection implements Parcelable { public TextSelection build() { return new TextSelection( mStartIndex, mEndIndex, mEntityConfidence, mId, - mExtras == null ? Bundle.EMPTY : mExtras); + mTextClassification, mExtras == null ? Bundle.EMPTY : mExtras); } } @@ -210,6 +244,7 @@ public final class TextSelection implements Parcelable { private final int mEndIndex; @Nullable private final LocaleList mDefaultLocales; private final boolean mDarkLaunchAllowed; + private final boolean mIncludeTextClassification; private final Bundle mExtras; @Nullable private SystemTextClassifierMetadata mSystemTcMetadata; @@ -219,12 +254,14 @@ public final class TextSelection implements Parcelable { int endIndex, LocaleList defaultLocales, boolean darkLaunchAllowed, + boolean includeTextClassification, Bundle extras) { mText = text; mStartIndex = startIndex; mEndIndex = endIndex; mDefaultLocales = defaultLocales; mDarkLaunchAllowed = darkLaunchAllowed; + mIncludeTextClassification = includeTextClassification; mExtras = extras; } @@ -303,6 +340,14 @@ public final class TextSelection implements Parcelable { } /** + * Returns true if the client wants the text classifier to classify the text as well and + * include a {@link TextClassification} object in the result. + */ + public boolean shouldIncludeTextClassification() { + return mIncludeTextClassification; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -320,9 +365,9 @@ public final class TextSelection implements Parcelable { private final CharSequence mText; private final int mStartIndex; private final int mEndIndex; - @Nullable private LocaleList mDefaultLocales; private boolean mDarkLaunchAllowed; + private boolean mIncludeTextClassification; private Bundle mExtras; /** @@ -372,6 +417,21 @@ public final class TextSelection implements Parcelable { } /** + * @param includeTextClassification If true, suggests the TextClassifier to classify the + * text in the suggested selection span and include a TextClassification object in + * the result. The TextClassifier may not support this and in which case, + * {@link TextSelection#getTextClassification()} returns {@code null}. + * + * @return this builder. + * @see TextSelection#getTextClassification() + */ + @NonNull + public Builder setIncludeTextClassification(boolean includeTextClassification) { + mIncludeTextClassification = includeTextClassification; + return this; + } + + /** * Sets the extended data. * * @return this builder @@ -389,6 +449,7 @@ public final class TextSelection implements Parcelable { public Request build() { return new Request(new SpannedString(mText), mStartIndex, mEndIndex, mDefaultLocales, mDarkLaunchAllowed, + mIncludeTextClassification, mExtras == null ? Bundle.EMPTY : mExtras); } } @@ -406,6 +467,7 @@ public final class TextSelection implements Parcelable { dest.writeParcelable(mDefaultLocales, flags); dest.writeBundle(mExtras); dest.writeParcelable(mSystemTcMetadata, flags); + dest.writeBoolean(mIncludeTextClassification); } private static Request readFromParcel(Parcel in) { @@ -415,9 +477,10 @@ public final class TextSelection implements Parcelable { final LocaleList defaultLocales = in.readParcelable(null); final Bundle extras = in.readBundle(); final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); + final boolean includeTextClassification = in.readBoolean(); final Request request = new Request(text, startIndex, endIndex, defaultLocales, - /* darkLaunchAllowed= */ false, extras); + /* darkLaunchAllowed= */ false, includeTextClassification, extras); request.setSystemTextClassifierMetadata(systemTcMetadata); return request; } @@ -448,6 +511,7 @@ public final class TextSelection implements Parcelable { mEntityConfidence.writeToParcel(dest, flags); dest.writeString(mId); dest.writeBundle(mExtras); + dest.writeParcelable(mTextClassification, flags); } public static final @android.annotation.NonNull Parcelable.Creator<TextSelection> CREATOR = @@ -469,5 +533,6 @@ public final class TextSelection implements Parcelable { mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in); mId = in.readString(); mExtras = in.readBundle(); + mTextClassification = in.readParcelable(TextClassification.class.getClassLoader()); } } diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index ea906c68c69b..ce29eea9fd0c 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -1409,6 +1409,11 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return mFilter; } + @Override + public CharSequence getAccessibilityClassName() { + return AutoCompleteTextView.class.getName(); + } + private class DropDownItemClickListener implements AdapterView.OnItemClickListener { public void onItemClick(AdapterView parent, View v, int position, long id) { performCompletion(v, position, id); diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index e08ccfddc4c5..6f189204434a 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -1124,6 +1124,7 @@ public final class SelectionActionModeHelper { mTrimmedText, mRelativeStart, mRelativeEnd) .setDefaultLocales(mDefaultLocales) .setDarkLaunchAllowed(true) + .setIncludeTextClassification(true) .build(); selection = mTextClassifier.get().suggestSelection(request); } else { @@ -1181,6 +1182,8 @@ public final class SelectionActionModeHelper { // Do not show smart actions for text containing unsupported characters. android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, ""); classification = TextClassification.EMPTY; + } else if (selection != null && selection.getTextClassification() != null) { + classification = selection.getTextClassification(); } else if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) { final TextClassification.Request request = diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index eda168dd8553..c4bdb5a72689 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -213,12 +213,12 @@ public class TaskOrganizer extends WindowOrganizer { private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() { @Override public void addStartingWindow(ActivityManager.RunningTaskInfo taskInfo, IBinder appToken) { - TaskOrganizer.this.addStartingWindow(taskInfo, appToken); + mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(taskInfo, appToken)); } @Override public void removeStartingWindow(ActivityManager.RunningTaskInfo taskInfo) { - TaskOrganizer.this.removeStartingWindow(taskInfo); + mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskInfo)); } @Override diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 2e3d560ccb56..9668c3b0af1c 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -162,6 +162,9 @@ public class ChooserActivity extends ResolverActivity implements private AppPredictor mWorkAppPredictor; private boolean mShouldDisplayLandscape; + private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4; + private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8; + @UnsupportedAppUsage public ChooserActivity() { } @@ -905,7 +908,7 @@ public class ChooserActivity extends ResolverActivity implements adapter, getPersonalProfileUserHandle(), /* workProfileUserHandle= */ null, - isSendAction(getTargetIntent())); + isSendAction(getTargetIntent()), getMaxTargetsPerRow()); } private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles( @@ -934,7 +937,7 @@ public class ChooserActivity extends ResolverActivity implements selectedProfile, getPersonalProfileUserHandle(), getWorkProfileUserHandle(), - isSendAction(getTargetIntent())); + isSendAction(getTargetIntent()), getMaxTargetsPerRow()); } private int findSelectedProfile() { @@ -2683,7 +2686,7 @@ public class ChooserActivity extends ResolverActivity implements // and b/150936654 recyclerView.setAdapter(gridAdapter); ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount( - gridAdapter.getMaxTargetsPerRow()); + getMaxTargetsPerRow()); } UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle(); @@ -2848,9 +2851,7 @@ public class ChooserActivity extends ResolverActivity implements @Override // ChooserListCommunicator public int getMaxRankedTargets() { - return mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() == null - ? ChooserGridAdapter.MAX_TARGETS_PER_ROW_PORTRAIT - : mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().getMaxTargetsPerRow(); + return getMaxTargetsPerRow(); } @Override // ChooserListCommunicator @@ -3198,6 +3199,13 @@ public class ChooserActivity extends ResolverActivity implements } } + int getMaxTargetsPerRow() { + int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT; + if (mShouldDisplayLandscape) { + maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE; + } + return maxTargets; + } /** * Adapter for all types of items and targets in ShareSheet. * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the @@ -3226,9 +3234,6 @@ public class ChooserActivity extends ResolverActivity implements private static final int VIEW_TYPE_CALLER_AND_RANK = 5; private static final int VIEW_TYPE_FOOTER = 6; - private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4; - private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8; - private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20; ChooserGridAdapter(ChooserListAdapter wrappedAdapter) { @@ -3277,14 +3282,6 @@ public class ChooserActivity extends ResolverActivity implements return false; } - int getMaxTargetsPerRow() { - int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT; - if (mShouldDisplayLandscape) { - maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE; - } - return maxTargets; - } - /** * Hides the list item content preview. * <p>Not to be confused with the sticky content preview which is above the @@ -3654,8 +3651,7 @@ public class ChooserActivity extends ResolverActivity implements position -= getSystemRowCount() + getProfileRowCount(); final int serviceCount = mChooserListAdapter.getServiceTargetCount(); - final int serviceRows = (int) Math.ceil((float) serviceCount - / ChooserListAdapter.MAX_SERVICE_TARGETS); + final int serviceRows = (int) Math.ceil((float) serviceCount / getMaxRankedTargets()); if (position < serviceRows) { return position * getMaxTargetsPerRow(); } diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java index 00b5cb646bca..570066807f16 100644 --- a/core/java/com/android/internal/app/ChooserListAdapter.java +++ b/core/java/com/android/internal/app/ChooserListAdapter.java @@ -82,8 +82,6 @@ public class ChooserListAdapter extends ResolverListAdapter { private static final int MAX_SERVICE_TARGET_APP = 8; private static final int DEFAULT_DIRECT_SHARE_RANKING_SCORE = 1000; - static final int MAX_SERVICE_TARGETS = 8; - /** {@link #getBaseScore} */ public static final float CALLER_TARGET_SCORE_BOOST = 900.f; /** {@link #getBaseScore} */ @@ -130,10 +128,10 @@ public class ChooserListAdapter extends ResolverListAdapter { super(context, payloadIntents, null, rList, filterLastUsed, resolverListController, chooserListCommunicator, false); - createPlaceHolders(); mMaxShortcutTargetsPerApp = context.getResources().getInteger(R.integer.config_maxShortcutTargetsPerApp); mChooserListCommunicator = chooserListCommunicator; + createPlaceHolders(); mSelectableTargetInfoCommunicator = selectableTargetInfoCommunicator; if (initialIntents != null) { @@ -227,7 +225,7 @@ public class ChooserListAdapter extends ResolverListAdapter { mParkingDirectShareTargets.clear(); mPendingChooserTargetService.clear(); mShortcutComponents.clear(); - for (int i = 0; i < MAX_SERVICE_TARGETS; i++) { + for (int i = 0; i < mChooserListCommunicator.getMaxRankedTargets(); i++) { mServiceTargets.add(mPlaceHolderTargetInfo); } } @@ -382,7 +380,7 @@ public class ChooserListAdapter extends ResolverListAdapter { public int getServiceTargetCount() { if (mChooserListCommunicator.isSendAction(mChooserListCommunicator.getTargetIntent()) && !ActivityManager.isLowRamDeviceStatic()) { - return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS); + return Math.min(mServiceTargets.size(), mChooserListCommunicator.getMaxRankedTargets()); } return 0; @@ -847,7 +845,8 @@ public class ChooserListAdapter extends ResolverListAdapter { int currentSize = mServiceTargets.size(); final float newScore = chooserTargetInfo.getModifiedScore(); - for (int i = 0; i < Math.min(currentSize, MAX_SERVICE_TARGETS); i++) { + for (int i = 0; i < Math.min(currentSize, mChooserListCommunicator.getMaxRankedTargets()); + i++) { final ChooserTargetInfo serviceTarget = mServiceTargets.get(i); if (serviceTarget == null) { mServiceTargets.set(i, chooserTargetInfo); @@ -858,7 +857,7 @@ public class ChooserListAdapter extends ResolverListAdapter { } } - if (currentSize < MAX_SERVICE_TARGETS) { + if (currentSize < mChooserListCommunicator.getMaxRankedTargets()) { mServiceTargets.add(chooserTargetInfo); return true; } diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java index 3a65a324f9d6..dd837fc2194c 100644 --- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java @@ -39,17 +39,19 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd private final ChooserProfileDescriptor[] mItems; private final boolean mIsSendAction; private int mBottomOffset; + private int mMaxTargetsPerRow; ChooserMultiProfilePagerAdapter(Context context, ChooserActivity.ChooserGridAdapter adapter, UserHandle personalProfileUserHandle, UserHandle workProfileUserHandle, - boolean isSendAction) { + boolean isSendAction, int maxTargetsPerRow) { super(context, /* currentPage */ 0, personalProfileUserHandle, workProfileUserHandle); mItems = new ChooserProfileDescriptor[] { createProfileDescriptor(adapter) }; mIsSendAction = isSendAction; + mMaxTargetsPerRow = maxTargetsPerRow; } ChooserMultiProfilePagerAdapter(Context context, @@ -58,7 +60,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd @Profile int defaultProfile, UserHandle personalProfileUserHandle, UserHandle workProfileUserHandle, - boolean isSendAction) { + boolean isSendAction, int maxTargetsPerRow) { super(context, /* currentPage */ defaultProfile, personalProfileUserHandle, workProfileUserHandle); mItems = new ChooserProfileDescriptor[] { @@ -66,6 +68,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd createProfileDescriptor(workAdapter) }; mIsSendAction = isSendAction; + mMaxTargetsPerRow = maxTargetsPerRow; } private ChooserProfileDescriptor createProfileDescriptor( @@ -114,7 +117,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd ChooserActivity.ChooserGridAdapter chooserGridAdapter = getItem(pageIndex).chooserGridAdapter; GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager(); - glm.setSpanCount(chooserGridAdapter.getMaxTargetsPerRow()); + glm.setSpanCount(mMaxTargetsPerRow); glm.setSpanSizeLookup( new GridLayoutManager.SpanSizeLookup() { @Override diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 6d98a5982c4f..0b047a2a3d47 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -19,6 +19,7 @@ package com.android.internal.app; import com.android.internal.os.BatteryStatsImpl; import android.bluetooth.BluetoothActivityEnergyInfo; +import android.os.BatteryUsageStats; import android.os.ParcelFileDescriptor; import android.os.WorkSource; import android.os.connectivity.CellularBatteryStats; @@ -49,6 +50,9 @@ interface IBatteryStats { void noteResetFlashlight(); // Remaining methods are only used in Java. + + BatteryUsageStats getBatteryUsageStats(); + @UnsupportedAppUsage byte[] getStatistics(); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index dcd74fd1ad3e..fb6eb97253e3 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -90,6 +90,8 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TimeUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.Display; @@ -112,7 +114,6 @@ import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayOutputStream; import java.io.File; @@ -10830,8 +10831,7 @@ public class BatteryStatsImpl extends BatteryStats { } final ByteArrayOutputStream memStream = new ByteArrayOutputStream(); try { - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(memStream, StandardCharsets.UTF_8.name()); + TypedXmlSerializer out = Xml.resolveSerializer(memStream); writeDailyItemsLocked(out); final long initialTimeMs = SystemClock.uptimeMillis() - startTimeMs; BackgroundThread.getHandler().post(new Runnable() { @@ -10861,15 +10861,15 @@ public class BatteryStatsImpl extends BatteryStats { } } - private void writeDailyItemsLocked(XmlSerializer out) throws IOException { + private void writeDailyItemsLocked(TypedXmlSerializer out) throws IOException { StringBuilder sb = new StringBuilder(64); out.startDocument(null, true); out.startTag(null, "daily-items"); for (int i=0; i<mDailyItems.size(); i++) { final DailyItem dit = mDailyItems.get(i); out.startTag(null, "item"); - out.attribute(null, "start", Long.toString(dit.mStartTime)); - out.attribute(null, "end", Long.toString(dit.mEndTime)); + out.attributeLong(null, "start", dit.mStartTime); + out.attributeLong(null, "end", dit.mEndTime); writeDailyLevelSteps(out, "dis", dit.mDischargeSteps, sb); writeDailyLevelSteps(out, "chg", dit.mChargeSteps, sb); if (dit.mPackageChanges != null) { @@ -10878,7 +10878,7 @@ public class BatteryStatsImpl extends BatteryStats { if (pc.mUpdate) { out.startTag(null, "upd"); out.attribute(null, "pkg", pc.mPackageName); - out.attribute(null, "ver", Long.toString(pc.mVersionCode)); + out.attributeLong(null, "ver", pc.mVersionCode); out.endTag(null, "upd"); } else { out.startTag(null, "rem"); @@ -10893,11 +10893,11 @@ public class BatteryStatsImpl extends BatteryStats { out.endDocument(); } - private void writeDailyLevelSteps(XmlSerializer out, String tag, LevelStepTracker steps, + private void writeDailyLevelSteps(TypedXmlSerializer out, String tag, LevelStepTracker steps, StringBuilder tmpBuilder) throws IOException { if (steps != null) { out.startTag(null, tag); - out.attribute(null, "n", Integer.toString(steps.mNumStepDurations)); + out.attributeInt(null, "n", steps.mNumStepDurations); for (int i=0; i<steps.mNumStepDurations; i++) { out.startTag(null, "s"); tmpBuilder.setLength(0); @@ -10919,10 +10919,9 @@ public class BatteryStatsImpl extends BatteryStats { return; } try { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(stream); readDailyItemsLocked(parser); - } catch (XmlPullParserException e) { + } catch (IOException e) { } finally { try { stream.close(); @@ -10931,7 +10930,7 @@ public class BatteryStatsImpl extends BatteryStats { } } - private void readDailyItemsLocked(XmlPullParser parser) { + private void readDailyItemsLocked(TypedXmlPullParser parser) { try { int type; while ((type = parser.next()) != XmlPullParser.START_TAG @@ -10975,17 +10974,11 @@ public class BatteryStatsImpl extends BatteryStats { } } - void readDailyItemTagLocked(XmlPullParser parser) throws NumberFormatException, + void readDailyItemTagLocked(TypedXmlPullParser parser) throws NumberFormatException, XmlPullParserException, IOException { DailyItem dit = new DailyItem(); - String attr = parser.getAttributeValue(null, "start"); - if (attr != null) { - dit.mStartTime = Long.parseLong(attr); - } - attr = parser.getAttributeValue(null, "end"); - if (attr != null) { - dit.mEndTime = Long.parseLong(attr); - } + dit.mStartTime = parser.getAttributeLong(null, "start", 0); + dit.mEndTime = parser.getAttributeLong(null, "end", 0); int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -11006,8 +10999,7 @@ public class BatteryStatsImpl extends BatteryStats { PackageChange pc = new PackageChange(); pc.mUpdate = true; pc.mPackageName = parser.getAttributeValue(null, "pkg"); - String verStr = parser.getAttributeValue(null, "ver"); - pc.mVersionCode = verStr != null ? Long.parseLong(verStr) : 0; + pc.mVersionCode = parser.getAttributeLong(null, "ver", 0); dit.mPackageChanges.add(pc); XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("rem")) { @@ -11028,16 +11020,15 @@ public class BatteryStatsImpl extends BatteryStats { mDailyItems.add(dit); } - void readDailyItemTagDetailsLocked(XmlPullParser parser, DailyItem dit, boolean isCharge, + void readDailyItemTagDetailsLocked(TypedXmlPullParser parser, DailyItem dit, boolean isCharge, String tag) throws NumberFormatException, XmlPullParserException, IOException { - final String numAttr = parser.getAttributeValue(null, "n"); - if (numAttr == null) { + final int num = parser.getAttributeInt(null, "n", -1); + if (num == -1) { Slog.w(TAG, "Missing 'n' attribute at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); return; } - final int num = Integer.parseInt(numAttr); LevelStepTracker steps = new LevelStepTracker(num); if (isCharge) { dit.mChargeSteps = steps; diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java new file mode 100644 index 000000000000..62e9f98181ab --- /dev/null +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.content.Context; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.Bundle; +import android.os.UidBatteryConsumer; +import android.os.UserManager; + +import java.util.List; + +/** + * Uses accumulated battery stats data and PowerCalculators to produce power + * usage data attributed to subsystems and UIDs. + */ +public class BatteryUsageStatsProvider { + private final Context mContext; + private final BatteryStatsImpl mStats; + + public BatteryUsageStatsProvider(Context context, BatteryStatsImpl stats) { + mContext = context; + mStats = stats; + } + + /** + * Returns a snapshot of battery attribution data. + */ + public BatteryUsageStats getBatteryUsageStats() { + + // TODO(b/174186345): instead of BatteryStatsHelper, use PowerCalculators directly. + final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext, + false /* collectBatteryBroadcast */); + batteryStatsHelper.create((Bundle) null); + final UserManager userManager = mContext.getSystemService(UserManager.class); + batteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, + userManager.getUserProfiles()); + + // TODO(b/174186358): read extra power component number from configuration + final int customPowerComponentCount = 0; + final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder() + .setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0)) + .setConsumedPower(batteryStatsHelper.getTotalPower()); + + final List<BatterySipper> usageList = batteryStatsHelper.getUsageList(); + for (int i = 0; i < usageList.size(); i++) { + final BatterySipper sipper = usageList.get(i); + if (sipper.drainType == BatterySipper.DrainType.APP) { + batteryUsageStatsBuilder.addUidBatteryConsumer( + new UidBatteryConsumer.Builder(customPowerComponentCount, sipper.getUid()) + .setPackageWithHighestDrain(sipper.packageWithHighestDrain) + .setConsumedPower(sipper.sumPower()) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, + sipper.cpuPowerMah) + .build()); + } + } + return batteryUsageStatsBuilder.build(); + } +} diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java index f668bbaf9438..e595db384cb9 100644 --- a/core/java/com/android/internal/os/KernelWakelockReader.java +++ b/core/java/com/android/internal/os/KernelWakelockReader.java @@ -184,6 +184,7 @@ public class KernelWakelockReader { try { wlStats = mSuspendControlService.getWakeLockStats(); + Slog.i(TAG, "Number of wakelock obtained from SystemSuspend: " + wlStats.length); updateWakelockStats(wlStats, staleStats); } catch (RemoteException e) { Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e); diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index 44dca9bae3da..854fb17e692b 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -69,6 +69,6 @@ oneway interface IPhoneStateListener { void onRegistrationFailed(in CellIdentity cellIdentity, String chosenPlmn, int domain, int causeCode, int additionalCauseCode); void onBarringInfoChanged(in BarringInfo barringInfo); - void onPhysicalChannelConfigurationChanged(in List<PhysicalChannelConfig> configs); - + void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs); + void onDataEnabledChanged(boolean enabled, int reason); } diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index a28a66376497..c2cbc04fcee5 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -41,16 +41,9 @@ interface ITelephonyRegistry { IOnSubscriptionsChangedListener callback); void removeOnSubscriptionsChangedListener(String pkg, IOnSubscriptionsChangedListener callback); - /** - * @deprecated Use {@link #listenWithFeature(String, String, IPhoneStateListener, int, - * boolean) instead - */ - @UnsupportedAppUsage - void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow); - void listenWithFeature(String pkg, String featureId, IPhoneStateListener callback, long events, - boolean notifyNow); - void listenForSubscriber(in int subId, String pkg, String featureId, - IPhoneStateListener callback, long events, boolean notifyNow); + + void listenWithEventList(in int subId, String pkg, String featureId, + IPhoneStateListener callback, in int[] events, boolean notifyNow); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void notifyCallStateForAllSubs(int state, String incomingNumber); void notifyCallState(in int phoneId, in int subId, int state, String incomingNumber); @@ -99,6 +92,6 @@ interface ITelephonyRegistry { void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity, String chosenPlmn, int domain, int causeCode, int additionalCauseCode); void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo); - void notifyPhysicalChannelConfigurationForSubscriber(in int subId, + void notifyPhysicalChannelConfigForSubscriber(in int subId, in List<PhysicalChannelConfig> configs); } diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index e067f5fbdf45..5388235df664 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -208,6 +208,13 @@ public class ArrayUtils { } /** + * Length of the given map or 0 if it's null. + */ + public static int size(@Nullable Map<?, ?> map) { + return map == null ? 0 : map.size(); + } + + /** * Checks that value is present as at least one of the elements of the array. * @param array the array to check in * @param value the value to check for diff --git a/core/java/com/android/internal/util/BinaryXmlSerializer.java b/core/java/com/android/internal/util/BinaryXmlSerializer.java index d3fcf71ba399..bc3b1f8bef57 100644 --- a/core/java/com/android/internal/util/BinaryXmlSerializer.java +++ b/core/java/com/android/internal/util/BinaryXmlSerializer.java @@ -69,7 +69,7 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer { * {@code ABX_}, representing "Android Binary XML." The final byte is a * version number which may be incremented as the protocol changes. */ - static final byte[] PROTOCOL_MAGIC_VERSION_0 = new byte[] { 0x41, 0x42, 0x58, 0x00 }; + public static final byte[] PROTOCOL_MAGIC_VERSION_0 = new byte[] { 0x41, 0x42, 0x58, 0x00 }; /** * Internal token which represents an attribute associated with the most diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index 8eb0e280350f..244efc596926 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -53,6 +53,7 @@ import java.util.Set; public class XmlUtils { private static final String STRING_ARRAY_SEPARATOR = ":"; + @SuppressWarnings("AndroidFrameworkEfficientXml") private static class ForcedTypedXmlSerializer extends XmlSerializerWrapper implements TypedXmlSerializer { public ForcedTypedXmlSerializer(XmlSerializer wrapped) { @@ -133,6 +134,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") private static class ForcedTypedXmlPullParser extends XmlPullParserWrapper implements TypedXmlPullParser { public ForcedTypedXmlPullParser(XmlPullParser wrapped) { @@ -1715,6 +1717,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) { if (in instanceof TypedXmlPullParser) { return ((TypedXmlPullParser) in).getAttributeInt(null, name, defaultValue); @@ -1730,6 +1733,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static int readIntAttribute(XmlPullParser in, String name) throws IOException { if (in instanceof TypedXmlPullParser) { try { @@ -1746,6 +1750,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static void writeIntAttribute(XmlSerializer out, String name, int value) throws IOException { if (out instanceof TypedXmlSerializer) { @@ -1755,6 +1760,7 @@ public class XmlUtils { out.attribute(null, name, Integer.toString(value)); } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) { if (in instanceof TypedXmlPullParser) { return ((TypedXmlPullParser) in).getAttributeLong(null, name, defaultValue); @@ -1770,6 +1776,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static long readLongAttribute(XmlPullParser in, String name) throws IOException { if (in instanceof TypedXmlPullParser) { try { @@ -1786,6 +1793,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static void writeLongAttribute(XmlSerializer out, String name, long value) throws IOException { if (out instanceof TypedXmlSerializer) { @@ -1795,6 +1803,7 @@ public class XmlUtils { out.attribute(null, name, Long.toString(value)); } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static float readFloatAttribute(XmlPullParser in, String name) throws IOException { if (in instanceof TypedXmlPullParser) { try { @@ -1811,6 +1820,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static void writeFloatAttribute(XmlSerializer out, String name, float value) throws IOException { if (out instanceof TypedXmlSerializer) { @@ -1820,10 +1830,12 @@ public class XmlUtils { out.attribute(null, name, Float.toString(value)); } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static boolean readBooleanAttribute(XmlPullParser in, String name) { return readBooleanAttribute(in, name, false); } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static boolean readBooleanAttribute(XmlPullParser in, String name, boolean defaultValue) { if (in instanceof TypedXmlPullParser) { @@ -1837,6 +1849,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value) throws IOException { if (out instanceof TypedXmlSerializer) { @@ -1869,6 +1882,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static byte[] readByteArrayAttribute(XmlPullParser in, String name) { if (in instanceof TypedXmlPullParser) { try { @@ -1885,6 +1899,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static void writeByteArrayAttribute(XmlSerializer out, String name, byte[] value) throws IOException { if (value != null) { diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java index 21021457377a..362fd7b4e937 100644 --- a/core/java/com/android/internal/widget/DecorCaptionView.java +++ b/core/java/com/android/internal/widget/DecorCaptionView.java @@ -18,9 +18,7 @@ package com.android.internal.widget; import android.content.Context; import android.graphics.Rect; -import android.os.RemoteException; import android.util.AttributeSet; -import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; @@ -72,7 +70,6 @@ import java.util.ArrayList; */ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, GestureDetector.OnGestureListener { - private final static String TAG = "DecorCaptionView"; private PhoneWindow mOwner = null; private boolean mShow = false; @@ -327,11 +324,7 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, private void toggleFreeformWindowingMode() { Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback(); if (callback != null) { - try { - callback.toggleFreeformWindowingMode(); - } catch (RemoteException ex) { - Log.e(TAG, "Cannot change task workspace."); - } + callback.toggleFreeformWindowingMode(); } } diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index ac2361dff560..b4d8e506c61a 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -35,24 +35,23 @@ import android.text.TextUtils; import android.util.AtomicFile; import android.util.EventLog; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Iterator; import java.util.regex.Matcher; @@ -712,8 +711,7 @@ public class BootReceiver extends BroadcastReceiver { HashMap<String, Long> timestamps = new HashMap<String, Long>(); boolean success = false; try (final FileInputStream stream = sFile.openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(stream); int type; while ((type = parser.next()) != XmlPullParser.START_TAG @@ -735,8 +733,7 @@ public class BootReceiver extends BroadcastReceiver { String tagName = parser.getName(); if (tagName.equals("log")) { final String filename = parser.getAttributeValue(null, "filename"); - final long timestamp = Long.valueOf(parser.getAttributeValue( - null, "timestamp")); + final long timestamp = parser.getAttributeLong(null, "timestamp"); timestamps.put(filename, timestamp); } else { Slog.w(TAG, "Unknown tag: " + parser.getName()); @@ -775,8 +772,7 @@ public class BootReceiver extends BroadcastReceiver { } try { - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(stream, StandardCharsets.UTF_8.name()); + TypedXmlSerializer out = Xml.resolveSerializer(stream); out.startDocument(null, true); out.startTag(null, "log-files"); @@ -785,7 +781,7 @@ public class BootReceiver extends BroadcastReceiver { String filename = itor.next(); out.startTag(null, "log"); out.attribute(null, "filename", filename); - out.attribute(null, "timestamp", timestamps.get(filename).toString()); + out.attributeLong(null, "timestamp", timestamps.get(filename)); out.endTag(null, "log"); } diff --git a/core/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java index c7c423b82cf1..4d1949e3e3d7 100644 --- a/core/java/com/android/server/backup/PermissionBackupHelper.java +++ b/core/java/com/android/server/backup/PermissionBackupHelper.java @@ -17,8 +17,8 @@ package com.android.server.backup; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.backup.BlobBackupHelper; -import android.os.UserHandle; import android.permission.PermissionManagerInternal; import android.util.Slog; @@ -34,14 +34,14 @@ public class PermissionBackupHelper extends BlobBackupHelper { // key under which the permission-grant state blob is committed to backup private static final String KEY_PERMISSIONS = "permissions"; - private final @NonNull UserHandle mUser; + private final @UserIdInt int mUserId; private final @NonNull PermissionManagerInternal mPermissionManager; public PermissionBackupHelper(int userId) { super(STATE_VERSION, KEY_PERMISSIONS); - mUser = UserHandle.of(userId); + mUserId = userId; mPermissionManager = LocalServices.getService(PermissionManagerInternal.class); } @@ -53,7 +53,7 @@ public class PermissionBackupHelper extends BlobBackupHelper { try { switch (key) { case KEY_PERMISSIONS: - return mPermissionManager.backupRuntimePermissions(mUser); + return mPermissionManager.backupRuntimePermissions(mUserId); default: Slog.w(TAG, "Unexpected backup key " + key); @@ -72,7 +72,7 @@ public class PermissionBackupHelper extends BlobBackupHelper { try { switch (key) { case KEY_PERMISSIONS: - mPermissionManager.restoreRuntimePermissions(payload, mUser); + mPermissionManager.restoreRuntimePermissions(payload, mUserId); break; default: diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 5a859aabce7b..bf79a4472fa5 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -571,6 +571,9 @@ enum { MEMINFO_PAGE_TABLES, MEMINFO_KERNEL_STACK, MEMINFO_KERNEL_RECLAIMABLE, + MEMINFO_ACTIVE, + MEMINFO_INACTIVE, + MEMINFO_UNEVICTABLE, MEMINFO_COUNT }; diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index d18722049109..91131115da83 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -313,7 +313,41 @@ message ConstantsProto { // The minimum amount of time between quota check alarms. optional int64 min_quota_check_delay_ms = 23; - // Next tag: 24 + // The total session limit of the particular standby bucket. Apps in this standby bucket can + // only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free + // HPJs). + optional int64 hpj_limit_active_ms = 24; + // The total session limit of the particular standby bucket. Apps in this standby bucket can + // only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free + // HPJs). + optional int64 hpj_limit_working_ms = 25; + // The total session limit of the particular standby bucket. Apps in this standby bucket can + // only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free + // HPJs). + optional int64 hpj_limit_frequent_ms = 26; + // The total session limit of the particular standby bucket. Apps in this standby bucket can + // only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free + // HPJs). + optional int64 hpj_limit_rare_ms = 27; + // The total session limit of the particular standby bucket. Apps in this standby bucket can + // only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free + // HPJs). + optional int64 hpj_limit_restricted_ms = 28; + // The period of time used to calculate HPJ sessions. Apps can only have HPJ sessions + // totalling HPJ_LIMIT_<bucket>_MS within this period of time (without factoring in any + // rewards or free HPJs). + optional int64 hpj_window_size_ms = 29; + // Length of time used to split an app's top time into chunks. + optional int64 hpj_top_app_time_chunk_size_ms = 30; + // How much HPJ quota to give back to an app based on the number of top app time chunks + // it had. + optional int64 hpj_reward_top_app_ms = 31; + // How much HPJ quota to give back to an app based on each non-top user interaction. + optional int64 hpj_reward_interaction_ms = 32; + // How much HPJ quota to give back to an app based on each notification seen event. + optional int64 hpj_reward_notification_seen_ms = 33; + + // Next tag: 34 } optional QuotaController quota_controller = 24; @@ -560,6 +594,11 @@ message StateControllerProto { // The amount of time that this job has remaining in its quota. This // can be negative if the job is out of quota. optional int64 remaining_quota_ms = 6; + // True if the app has requested that this be a foreground job. + optional bool is_requested_foreground_job = 7; + // True if this job is within the foreground quota bounds and is therefore allowed to + // run as a foreground job. Valid only if is_foreground_requested_job is true. + optional bool is_within_fg_job_quota = 8; } repeated TrackedJob tracked_jobs = 4; @@ -665,6 +704,19 @@ message StateControllerProto { repeated JobStatusShortInfoProto running_jobs = 5; } + message TopAppTimer { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional Package pkg = 1; + // True if the Timer is actively tracking jobs. + optional bool is_active = 2; + // The time this timer last became active. Only valid if is_active is true. + optional int64 start_time_elapsed = 3; + // How many activities are currently in the RESUMED state. Valid only if is_active is + // true. + optional int32 activity_count = 4; + } + message PackageStats { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -677,6 +729,8 @@ message StateControllerProto { repeated ExecutionStats execution_stats = 4; reserved 5; // in_quota_alarm_listener + + optional Timer fg_job_timer = 6; } repeated PackageStats package_stats = 5; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 60dd0eb8a780..1250eb776176 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4047,6 +4047,13 @@ <permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" android:protectionLevel="signature" /> + <!-- Allows an application to modify the refresh rate switching type. This + matches Setting.Secure.MATCH_CONTENT_FRAME_RATE. + @hide + @TestApi --> + <permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE" + android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to control VPN. <p>Not for use by third-party applications.</p> @hide --> @@ -5255,7 +5262,8 @@ <attribution android:tag="TwilightService" android:label="@string/twilight_service"/> <!-- Attribution for the Offline LocationTimeZoneProvider, used to detect time zone using on-device data --> - <attribution android:tag="OfflineLocationTimeZoneProvider" android:label="@string/offline_location_time_zone_detection_service"/> + <attribution android:tag="OfflineLocationTimeZoneProvider" + android:label="@string/offline_location_time_zone_detection_service_attribution"/> <application android:process="system" android:persistent="true" diff --git a/core/res/res/drawable/view_accessibility_focused.xml b/core/res/res/drawable/view_accessibility_focused.xml index 025916b35f40..aa3031e5cf26 100644 --- a/core/res/res/drawable/view_accessibility_focused.xml +++ b/core/res/res/drawable/view_accessibility_focused.xml @@ -17,8 +17,8 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" > <stroke - android:width="4dp" - android:color="@color/accessibility_focus_highlight" /> + android:width="@dimen/accessibility_focus_highlight_stroke_width" + android:color="@color/accessibility_focus_highlight_color" /> <corners android:radius="2dp" /> diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml index c71e8863502c..60507eddba22 100644 --- a/core/res/res/layout/notification_top_line_views.xml +++ b/core/res/res/layout/notification_top_line_views.xml @@ -88,7 +88,7 @@ <DateTimeView android:id="@+id/time" - android:textAppearance="@style/TextAppearance.Material.Notification.Time" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="@dimen/notification_header_separating_margin" diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 0079d8cd0276..110e77a8aa05 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -159,7 +159,7 @@ <color name="keyguard_avatar_nick_color">#ffffffff</color> <color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color> - <color name="accessibility_focus_highlight">#bf39b500</color> + <color name="accessibility_focus_highlight_color">#bf39b500</color> <color name="autofilled_highlight">#4dffeb3b</color> <color name="system_notification_accent_color">#ff607D8B</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 37c3adbe5fcc..40e11cb92d3e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1613,21 +1613,28 @@ <!-- Whether the geolocation time zone detection feature is enabled. --> <bool name="config_enableGeolocationTimeZoneDetection" translatable="false">true</bool> - <!-- Whether to enable primary location time zone provider overlay which allows the primary - location time zone provider to be replaced by an app at run-time. When disabled, only the + <!-- Whether the primary LocationTimeZoneProvider is enabled device. + Ignored if config_enableGeolocationTimeZoneDetection is false --> + <bool name="config_enablePrimaryLocationTimeZoneProvider" translatable="false">false</bool> + <!-- Used when enablePrimaryLocationTimeZoneProvider is true. Controls whether to enable primary + location time zone provider overlay which allows the primary location time zone provider to + be replaced by an app at run-time. When disabled, only the config_primaryLocationTimeZoneProviderPackageName package will be searched for the primary location time zone provider, otherwise any system package is eligible. Anyone who wants to - disable the overlay mechanism can set it to false. --> + disable the runtime overlay mechanism can set it to false. --> <bool name="config_enablePrimaryLocationTimeZoneOverlay" translatable="false">false</bool> <!-- Package name providing the primary location time zone provider. Used only when config_enablePrimaryLocationTimeZoneOverlay is false. --> <string name="config_primaryLocationTimeZoneProviderPackageName" translatable="false">@null</string> - <!-- Whether to enable secondary location time zone provider overlay which allows the secondary - location time zone provider to be replaced by an app at run-time. When disabled, only the - config_secondaryLocationTimeZoneProviderPackageName package will be searched for the - secondary location time zone provider, otherwise any system package is eligible. Anyone who - wants to disable the overlay mechanism can set it to false. --> + <!-- Whether the secondary LocationTimeZoneProvider is enabled device. + Ignored if config_enableGeolocationTimeZoneDetection is false --> + <bool name="config_enableSecondaryLocationTimeZoneProvider" translatable="false">true</bool> + <!-- Used when enableSecondaryLocationTimeZoneProvider is true. Controls whether to enable + secondary location time zone provider overlay which allows the primary location time zone + provider to config_secondaryLocationTimeZoneProviderPackageName package will be searched + for the secondary location time zone provider, otherwise any system package is eligible. + Anyone who wants to disable the runtime overlay mechanism can set it to false. --> <bool name="config_enableSecondaryLocationTimeZoneOverlay" translatable="false">false</bool> <!-- Package name providing the secondary location time zone provider. Used only when config_enableSecondaryLocationTimeZoneOverlay is false. @@ -4515,7 +4522,7 @@ <integer name="config_pdp_reject_retry_delay_ms">-1</integer> <!-- Whether or not to enable the binder heavy hitter watcher by default --> - <bool name="config_defaultBinderHeavyHitterWatcherEnabled">true</bool> + <bool name="config_defaultBinderHeavyHitterWatcherEnabled">false</bool> <!-- The default batch size for the binder heavy hitter watcher --> <integer name="config_defaultBinderHeavyHitterWatcherBatchSize">2000</integer> @@ -4526,7 +4533,7 @@ </item> <!-- Whether or not to enable the binder heavy hitter auto sampler by default --> - <bool name="config_defaultBinderHeavyHitterAutoSamplerEnabled">true</bool> + <bool name="config_defaultBinderHeavyHitterAutoSamplerEnabled">false</bool> <!-- The default batch size for the binder heavy hitter auto sampler --> <integer name="config_defaultBinderHeavyHitterAutoSamplerBatchSize">400</integer> @@ -4562,4 +4569,7 @@ <!-- Indicates that default fitness tracker app needs to request sensor and location permissions. --> <bool name="config_trackerAppNeedsPermissions">false</bool> + + <!-- Component with platform query permissions for AppSearch --> + <string name="config_defaultAppSearchPlatformQuerierComponent" translatable="false"></string> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 4bcabff109ea..05db741643e8 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -553,6 +553,9 @@ <!-- Width of the outline stroke used by the accessibility screen magnification indicator --> <dimen name="accessibility_magnification_indicator_width">4dip</dimen> + <!-- Width of the outline stroke used by the accessibility focus rectangle --> + <dimen name="accessibility_focus_highlight_stroke_width">4dp</dimen> + <!-- Margin around the various security views --> <dimen name="keyguard_muliuser_selector_margin">8dp</dimen> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8e3a0cbdd10c..89b986b8fcb8 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -433,9 +433,12 @@ <string name="sensor_notification_service">Sensor Notification Service</string> <!-- Attribution for Twilight service. [CHAR LIMIT=NONE]--> <string name="twilight_service">Twilight Service</string> - <!-- Attribution for Offline LocationTimeZoneDetector service, i.e. one capable of performing - time zone lookup using geo-spacial information held on the device. [CHAR LIMIT=NONE]--> - <string name="offline_location_time_zone_detection_service">Offline Time Zone Detection Service</string> + <!-- Attribution for the Offline LocationTimeZoneProvider service, i.e. the service capable of + performing time zone detection using time zone geospatial information held on the device. + This text is shown in UIs related to an application name to help users and developers to + understand which sub-unit of an application is requesting permissions and using power. + [CHAR LIMIT=NONE]--> + <string name="offline_location_time_zone_detection_service_attribution">Time Zone Detector (No connectivity)</string> <!-- Factory reset warning dialog strings--> <skip /> <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e96439250696..e50eee6afa91 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2168,8 +2168,10 @@ <java-symbol type="string" name="config_deviceConfiguratorPackageName" /> <java-symbol type="array" name="config_autoTimeSourcesPriority" /> <java-symbol type="bool" name="config_enableGeolocationTimeZoneDetection" /> + <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneProvider" /> <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" /> <java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" /> + <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" /> <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneOverlay" /> <java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" /> @@ -4113,4 +4115,11 @@ <java-symbol type="bool" name="config_trackerAppNeedsPermissions"/> + <!-- Component with platform query permissions for AppSearch --> + <java-symbol type="string" name="config_defaultAppSearchPlatformQuerierComponent" /> + + <!-- Color used by the accessibility focus rectangle --> + <java-symbol type="color" name="accessibility_focus_highlight_color" /> + <!-- Width of the outline stroke used by the accessibility focus rectangle --> + <java-symbol type="dimen" name="accessibility_focus_highlight_stroke_width" /> </resources> diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java new file mode 100644 index 000000000000..e03cea31f077 --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java @@ -0,0 +1,65 @@ +/* + * 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.appsearch; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.expectThrows; + +import org.junit.Test; + +public class SetSchemaRequestTest { + + @Test + public void testInvalidSchemaReferences() { + IllegalArgumentException expected = + expectThrows( + IllegalArgumentException.class, + () -> + new SetSchemaRequest.Builder() + .setSchemaTypeVisibilityForSystemUi(false, "InvalidSchema") + .build()); + assertThat(expected).hasMessageThat().contains("referenced, but were not added"); + } + + @Test + public void testSchemaTypeVisibilityForSystemUi_Visible() { + AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); + + // By default, the schema is visible. + SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(schema).build(); + assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty(); + + request = + new SetSchemaRequest.Builder() + .addSchema(schema) + .setSchemaTypeVisibilityForSystemUi(true, "Schema") + .build(); + assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty(); + } + + @Test + public void testSchemaTypeVisibilityForSystemUi_NotVisible() { + AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); + SetSchemaRequest request = + new SetSchemaRequest.Builder() + .addSchema(schema) + .setSchemaTypeVisibilityForSystemUi(false, "Schema") + .build(); + assertThat(request.getSchemasNotPlatformSurfaceable()).containsExactly("Schema"); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java index 2eaebd6e7b93..7072a8161a87 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java @@ -78,4 +78,125 @@ public class AppSearchSchemaCtsTest { .build())); assertThat(e).hasMessageThat().contains("Property defined more than once: subject"); } + + @Test + public void testEquals_identical() { + AppSearchSchema schema1 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + AppSearchSchema schema2 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + assertThat(schema1).isEqualTo(schema2); + assertThat(schema1.hashCode()).isEqualTo(schema2.hashCode()); + } + + @Test + public void testEquals_differentOrder() { + AppSearchSchema schema1 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + AppSearchSchema schema2 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .build()) + .build(); + assertThat(schema1).isEqualTo(schema2); + assertThat(schema1.hashCode()).isEqualTo(schema2.hashCode()); + } + + @Test + public void testEquals_failure() { + AppSearchSchema schema1 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + AppSearchSchema schema2 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + PropertyConfig + .INDEXING_TYPE_EXACT_TERMS) // Different + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + assertThat(schema1).isNotEqualTo(schema2); + assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode()); + } + + @Test + public void testEquals_failure_differentOrder() { + AppSearchSchema schema1 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new PropertyConfig.Builder("body") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + // Order of 'body' and 'subject' has been switched + AppSearchSchema schema2 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("body") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + assertThat(schema1).isNotEqualTo(schema2); + assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode()); + } } diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java new file mode 100644 index 000000000000..cfcfcc8cf044 --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java @@ -0,0 +1,254 @@ +/* + * 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.appsearch.util; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Build; +import android.os.Bundle; +import android.os.ParcelUuid; +import android.os.Parcelable; +import android.util.Size; +import android.util.SizeF; +import android.util.SparseArray; + +import com.google.common.collect.ImmutableList; + +import org.junit.Test; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.UUID; + +public class BundleUtilTest { + @Test + public void testDeepEquals_self() { + Bundle one = new Bundle(); + one.putString("a", "a"); + assertThat(BundleUtil.deepEquals(one, one)).isTrue(); + } + + @Test + public void testDeepEquals_simple() { + Bundle one = new Bundle(); + one.putString("a", "a"); + + Bundle two = new Bundle(); + two.putString("a", "a"); + + assertThat(one).isNotEqualTo(two); + assertThat(BundleUtil.deepEquals(one, two)).isTrue(); + } + + @Test + public void testDeepEquals_keyMismatch() { + Bundle one = new Bundle(); + one.putString("a", "a"); + + Bundle two = new Bundle(); + two.putString("a", "a"); + two.putString("b", "b"); + assertThat(BundleUtil.deepEquals(one, two)).isFalse(); + } + + @Test + public void testDeepEquals_thorough_equal() { + Bundle[] inputs = new Bundle[2]; + for (int i = 0; i < 2; i++) { + inputs[i] = createThoroughBundle(); + } + assertThat(inputs[0]).isNotEqualTo(inputs[1]); + assertThat(BundleUtil.deepEquals(inputs[0], inputs[1])).isTrue(); + } + + @Test + public void testDeepEquals_thorough_notEqual() { + Bundle[] inputs = new Bundle[2]; + for (int i = 0; i < 2; i++) { + Bundle b = createThoroughBundle(); + // Create a difference + assertThat(b.containsKey("doubleArray")).isTrue(); + b.putDoubleArray("doubleArray", new double[] {18., i}); + inputs[i] = b; + } + assertThat(inputs[0]).isNotEqualTo(inputs[1]); + assertThat(BundleUtil.deepEquals(inputs[0], inputs[1])).isFalse(); + } + + @Test + public void testDeepEquals_nestedNotEquals() { + Bundle one = new Bundle(); + one.putString("a", "a"); + Bundle two = new Bundle(); + two.putBundle("b", one); + Bundle twoClone = new Bundle(); + twoClone.putBundle("b", one); + Bundle three = new Bundle(); + three.putBundle("b", two); + + ArrayList<Bundle> listOne = new ArrayList<>(ImmutableList.of(one, two, three)); + ArrayList<Bundle> listOneClone = new ArrayList<>(ImmutableList.of(one, twoClone, three)); + ArrayList<Bundle> listTwo = new ArrayList<>(ImmutableList.of(one, three, two)); + Bundle b1 = new Bundle(); + b1.putParcelableArrayList("key", listOne); + Bundle b1Clone = new Bundle(); + b1Clone.putParcelableArrayList("key", listOneClone); + Bundle b2 = new Bundle(); + b2.putParcelableArrayList("key", listTwo); + + assertThat(b1).isNotEqualTo(b1Clone); + assertThat(BundleUtil.deepEquals(b1, b1Clone)).isTrue(); + assertThat(BundleUtil.deepEquals(b1, b2)).isFalse(); + assertThat(BundleUtil.deepEquals(b1Clone, b2)).isFalse(); + } + + @Test + public void testDeepEquals_sparseArray() { + Parcelable parcelable1 = new ParcelUuid(UUID.randomUUID()); + Parcelable parcelable2 = new ParcelUuid(UUID.randomUUID()); + Parcelable parcelable3 = new ParcelUuid(UUID.randomUUID()); + + SparseArray<Parcelable> array1 = new SparseArray<>(); + array1.put(1, parcelable1); + array1.put(10, parcelable2); + + SparseArray<Parcelable> array1Clone = new SparseArray<>(); + array1Clone.put(1, parcelable1); + array1Clone.put(10, parcelable2); + + SparseArray<Parcelable> array2 = new SparseArray<>(); + array2.put(1, parcelable1); + array2.put(10, parcelable3); // Different + + Bundle b1 = new Bundle(); + b1.putSparseParcelableArray("array1", array1); + Bundle b1Clone = new Bundle(); + b1Clone.putSparseParcelableArray("array1", array1Clone); + Bundle b2 = new Bundle(); + b2.putSparseParcelableArray("array1", array2); + + assertThat(b1).isNotEqualTo(b1Clone); + assertThat(BundleUtil.deepEquals(b1, b1Clone)).isTrue(); + assertThat(BundleUtil.deepEquals(b1, b2)).isFalse(); + assertThat(BundleUtil.deepEquals(b1Clone, b2)).isFalse(); + } + + @Test + public void testDeepHashCode_same() { + Bundle[] inputs = new Bundle[2]; + for (int i = 0; i < 2; i++) { + inputs[i] = createThoroughBundle(); + } + assertThat(BundleUtil.deepHashCode(inputs[0])) + .isEqualTo(BundleUtil.deepHashCode(inputs[1])); + } + + @Test + public void testDeepHashCode_different() { + Bundle[] inputs = new Bundle[2]; + for (int i = 0; i < 2; i++) { + Bundle b = createThoroughBundle(); + // Create a difference + assertThat(b.containsKey("doubleArray")).isTrue(); + b.putDoubleArray("doubleArray", new double[] {18., i}); + inputs[i] = b; + } + assertThat(BundleUtil.deepHashCode(inputs[0])) + .isNotEqualTo(BundleUtil.deepHashCode(inputs[1])); + } + + @Test + public void testHashCode_sparseArray() { + Parcelable parcelable1 = new ParcelUuid(UUID.randomUUID()); + Parcelable parcelable2 = new ParcelUuid(UUID.randomUUID()); + Parcelable parcelable3 = new ParcelUuid(UUID.randomUUID()); + + SparseArray<Parcelable> array1 = new SparseArray<>(); + array1.put(1, parcelable1); + array1.put(10, parcelable2); + + SparseArray<Parcelable> array1Clone = new SparseArray<>(); + array1Clone.put(1, parcelable1); + array1Clone.put(10, parcelable2); + + SparseArray<Parcelable> array2 = new SparseArray<>(); + array2.put(1, parcelable1); + array2.put(10, parcelable3); // Different + + Bundle b1 = new Bundle(); + b1.putSparseParcelableArray("array1", array1); + Bundle b1Clone = new Bundle(); + b1Clone.putSparseParcelableArray("array1", array1Clone); + Bundle b2 = new Bundle(); + b2.putSparseParcelableArray("array1", array2); + + assertThat(b1.hashCode()).isNotEqualTo(b1Clone.hashCode()); + assertThat(BundleUtil.deepHashCode(b1)).isEqualTo(BundleUtil.deepHashCode(b1Clone)); + assertThat(BundleUtil.deepHashCode(b1)).isNotEqualTo(BundleUtil.deepHashCode(b2)); + } + + private static Bundle createThoroughBundle() { + Bundle toy1 = new Bundle(); + toy1.putString("a", "a"); + Bundle toy2 = new Bundle(); + toy2.putInt("b", 2); + + Bundle b = new Bundle(); + // BaseBundle stuff + b.putBoolean("boolean", true); + b.putByte("byte", (byte) 1); + b.putChar("char", 'a'); + b.putShort("short", (short) 2); + b.putInt("int", 3); + b.putLong("long", 4L); + b.putFloat("float", 5f); + b.putDouble("double", 6f); + b.putString("string", "b"); + b.putCharSequence("charSequence", "c"); + b.putIntegerArrayList("integerArrayList", new ArrayList<>(ImmutableList.of(7, 8))); + b.putStringArrayList("stringArrayList", new ArrayList<>(ImmutableList.of("d", "e"))); + b.putCharSequenceArrayList( + "charSequenceArrayList", new ArrayList<>(ImmutableList.of("f", "g"))); + b.putSerializable("serializable", new BigDecimal(9)); + b.putBooleanArray("booleanArray", new boolean[] {true, false, true}); + b.putByteArray("byteArray", new byte[] {(byte) 10, (byte) 11}); + b.putShortArray("shortArray", new short[] {(short) 12, (short) 13}); + b.putCharArray("charArray", new char[] {'h', 'i'}); + b.putLongArray("longArray", new long[] {14L, 15L}); + b.putFloatArray("floatArray", new float[] {16f, 17f}); + b.putDoubleArray("doubleArray", new double[] {18., 19.}); + b.putStringArray("stringArray", new String[] {"j", "k"}); + b.putCharSequenceArray("charSequenceArrayList", new CharSequence[] {"l", "m"}); + + // Bundle stuff + b.putParcelable("parcelable", toy1); + if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + b.putSize("size", new Size(20, 21)); + b.putSizeF("sizeF", new SizeF(22f, 23f)); + } + b.putParcelableArray("parcelableArray", new Parcelable[] {toy1, toy2}); + b.putParcelableArrayList( + "parcelableArrayList", new ArrayList<>(ImmutableList.of(toy1, toy2))); + SparseArray<Parcelable> sparseArray = new SparseArray<>(); + sparseArray.put(24, toy1); + sparseArray.put(1025, toy2); + b.putSparseParcelableArray("sparceParcelableArray", sparseArray); + b.putBundle("bundle", toy1); + + return b; + } +} diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java index 365e97ded928..e04d9034d2c1 100644 --- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java +++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java @@ -24,12 +24,12 @@ import android.os.UserHandle; import android.test.AndroidTestCase; import android.util.AttributeSet; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import androidx.test.filters.LargeTest; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.File; @@ -308,12 +308,12 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { static class TestSerializer implements XmlSerializerAndParser<TestServiceType> { - public void writeAsXml(TestServiceType item, XmlSerializer out) throws IOException { + public void writeAsXml(TestServiceType item, TypedXmlSerializer out) throws IOException { out.attribute(null, "type", item.type); out.attribute(null, "value", item.value); } - public TestServiceType createFromXml(XmlPullParser parser) + public TestServiceType createFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final String type = parser.getAttributeValue(null, "type"); final String value = parser.getAttributeValue(null, "value"); diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java index 4370462279b2..e750454a01ff 100644 --- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java +++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java @@ -23,18 +23,16 @@ import static org.junit.Assert.fail; import android.os.Parcel; import android.util.Pair; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.util.FastXmlSerializer; - import org.junit.Test; import org.junit.runner.RunWith; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -163,7 +161,7 @@ public class BrightnessConfigurationTest { BrightnessConfiguration config = builder.build(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - XmlSerializer out = new FastXmlSerializer(); + TypedXmlSerializer out = Xml.newFastSerializer(); out.setOutput(baos, StandardCharsets.UTF_8.name()); out.startDocument(null, true); config.saveToXml(out); @@ -171,7 +169,7 @@ public class BrightnessConfigurationTest { baos.flush(); ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray()); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(input, StandardCharsets.UTF_8.name()); BrightnessConfiguration loadedConfig = BrightnessConfiguration.loadFromXml(parser); diff --git a/core/tests/coretests/src/android/util/BinaryXmlTest.java b/core/tests/coretests/src/android/util/BinaryXmlTest.java index be63a0ecc65f..6fa5a2343361 100644 --- a/core/tests/coretests/src/android/util/BinaryXmlTest.java +++ b/core/tests/coretests/src/android/util/BinaryXmlTest.java @@ -20,6 +20,8 @@ import static android.util.XmlTest.assertNext; import static android.util.XmlTest.buildPersistableBundle; import static android.util.XmlTest.doPersistableBundleRead; import static android.util.XmlTest.doPersistableBundleWrite; +import static android.util.XmlTest.doVerifyRead; +import static android.util.XmlTest.doVerifyWrite; import static org.junit.Assert.assertEquals; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -33,6 +35,11 @@ import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; @RunWith(AndroidJUnit4.class) @@ -96,4 +103,58 @@ public class BinaryXmlTest { final PersistableBundle actual = doPersistableBundleRead(secondIn, os.toByteArray()); assertEquals(expected.toString(), actual.toString()); } + + @Test + public void testResolve_File() throws Exception { + { + final File file = File.createTempFile("fast", ".xml"); + try (OutputStream os = new FileOutputStream(file)) { + TypedXmlSerializer xml = Xml.newFastSerializer(); + xml.setOutput(os, StandardCharsets.UTF_8.name()); + doVerifyWrite(xml); + } + try (InputStream is = new FileInputStream(file)) { + doVerifyRead(Xml.resolvePullParser(is)); + } + } + { + final File file = File.createTempFile("binary", ".xml"); + try (OutputStream os = new FileOutputStream(file)) { + TypedXmlSerializer xml = Xml.newBinarySerializer(); + xml.setOutput(os, StandardCharsets.UTF_8.name()); + doVerifyWrite(xml); + } + try (InputStream is = new FileInputStream(file)) { + doVerifyRead(Xml.resolvePullParser(is)); + } + } + } + + @Test + public void testResolve_Memory() throws Exception { + { + final byte[] data; + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + TypedXmlSerializer xml = Xml.newFastSerializer(); + xml.setOutput(os, StandardCharsets.UTF_8.name()); + doVerifyWrite(xml); + data = os.toByteArray(); + } + try (InputStream is = new ByteArrayInputStream(data)) { + doVerifyRead(Xml.resolvePullParser(is)); + } + } + { + final byte[] data; + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + TypedXmlSerializer xml = Xml.newBinarySerializer(); + xml.setOutput(os, StandardCharsets.UTF_8.name()); + doVerifyWrite(xml); + data = os.toByteArray(); + } + try (InputStream is = new ByteArrayInputStream(data)) { + doVerifyRead(Xml.resolvePullParser(is)); + } + } + } } diff --git a/core/tests/coretests/src/android/util/XmlTest.java b/core/tests/coretests/src/android/util/XmlTest.java index 5ae17c46944f..a8fc6f933605 100644 --- a/core/tests/coretests/src/android/util/XmlTest.java +++ b/core/tests/coretests/src/android/util/XmlTest.java @@ -205,7 +205,7 @@ public class XmlTest { private static final byte[] TEST_BYTES = new byte[] { 0, 1, 2, 3, 4, 3, 2, 1, 0 }; private static final byte[] TEST_BYTES_EMPTY = new byte[0]; - private static void doVerifyWrite(TypedXmlSerializer out) throws Exception { + static void doVerifyWrite(TypedXmlSerializer out) throws Exception { out.startDocument(StandardCharsets.UTF_8.name(), true); out.startTag(null, "one"); { @@ -244,7 +244,7 @@ public class XmlTest { out.endDocument(); } - private static void doVerifyRead(TypedXmlPullParser in) throws Exception { + static void doVerifyRead(TypedXmlPullParser in) throws Exception { assertEquals(START_DOCUMENT, in.getEventType()); assertNext(in, START_TAG, "one", 1); { diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java index 8e2490789a6f..75116d8c6df2 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java @@ -38,6 +38,7 @@ import android.os.UserHandle; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.R; import com.android.internal.util.IntPair; import com.android.server.accessibility.test.MessageCapturingHandler; @@ -73,12 +74,18 @@ public class AccessibilityManagerTest { @Mock private IAccessibilityManager mMockService; private MessageCapturingHandler mHandler; private Instrumentation mInstrumentation; + private int mFocusStrokeWidthDefaultValue; + private int mFocusColorDefaultValue; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mHandler = new MessageCapturingHandler(null); mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mFocusStrokeWidthDefaultValue = mInstrumentation.getContext().getResources() + .getDimensionPixelSize(R.dimen.accessibility_focus_highlight_stroke_width); + mFocusColorDefaultValue = mInstrumentation.getContext().getResources().getColor( + R.color.accessibility_focus_highlight_color); } @After @@ -94,8 +101,12 @@ public class AccessibilityManagerTest { when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt())) .thenReturn(serviceReturnValue); + when(mMockService.getFocusStrokeWidth()).thenReturn(mFocusStrokeWidthDefaultValue); + when(mMockService.getFocusColor()).thenReturn(mFocusColorDefaultValue); + AccessibilityManager manager = - new AccessibilityManager(mHandler, mMockService, UserHandle.USER_CURRENT); + new AccessibilityManager(mInstrumentation.getContext(), mHandler, mMockService, + UserHandle.USER_CURRENT, true); verify(mMockService).addClient(any(IAccessibilityManagerClient.class), anyInt()); mHandler.setCallback(manager.getCallback()); @@ -205,4 +216,16 @@ public class AccessibilityManagerTest { verify(mMockService).setWindowMagnificationConnection(connection); } + + @Test + public void testGetDefaultValueOfFocusAppearanceData() { + AccessibilityManager manager = + new AccessibilityManager(mInstrumentation.getContext(), mHandler, null, + UserHandle.USER_CURRENT, false); + + assertEquals(mFocusStrokeWidthDefaultValue, + manager.getAccessibilityFocusStrokeWidth()); + assertEquals(mFocusColorDefaultValue, + manager.getAccessibilityFocusColor()); + } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 75a7504cac2f..cfdb2b769a08 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -161,4 +161,6 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public void setTouchExplorationPassthroughRegion(int displayId, Region region) {} public void setGestureDetectionPassthroughRegion(int displayId, Region region) {} + + public void setFocusAppearance(int strokeWidth, int color) {} } diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index c17c36eba2dc..6baf3056be32 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -68,6 +68,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import android.widget.Toast; +import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; @@ -148,7 +149,8 @@ public class AccessibilityShortcutControllerTest { // Use the extra level of indirection in the object to mock framework objects AccessibilityManager accessibilityManager = - new AccessibilityManager(mHandler, mAccessibilityManagerService, 0); + new AccessibilityManager(InstrumentationRegistry.getContext(), mHandler, + mAccessibilityManagerService, 0, true); when(mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext)) .thenReturn(accessibilityManager); when(mContext.getSystemService(Context.ACCESSIBILITY_SERVICE)) diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index 46695d2764dd..a74f580b65e6 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -38,13 +38,11 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.app.Activity; -import android.app.ActivityTaskManager; +import android.app.ActivityClient; import android.app.ActivityThread; import android.app.ActivityThread.ActivityClientRecord; -import android.app.IActivityTaskManager; import android.app.LoadedApk; import android.app.servertransaction.PendingTransactionActions; import android.content.ComponentName; @@ -54,7 +52,6 @@ import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.IBinder; -import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.testing.PollingCheck; @@ -224,18 +221,19 @@ public class ActivityThreadClientTest { private MockitoSession mMockSession; private ActivityThread mThread; - private ClientMockSession() throws RemoteException { + private ClientMockSession() { mThread = ActivityThread.currentActivityThread(); mMockSession = mockitoSession() .strictness(Strictness.LENIENT) - .spyStatic(ActivityTaskManager.class) + .spyStatic(ActivityClient.class) .spyStatic(WindowManagerGlobal.class) .startMocking(); doReturn(Mockito.mock(WindowManagerGlobal.class)) .when(WindowManagerGlobal::getInstance); - IActivityTaskManager mockAtm = Mockito.mock(IActivityTaskManager.class); - doReturn(mockAtm).when(ActivityTaskManager::getService); - when(mockAtm.finishActivity(any(), anyInt(), any(), anyInt())).thenReturn(true); + final ActivityClient mockAc = Mockito.mock(ActivityClient.class); + doReturn(mockAc).when(ActivityClient::getInstance); + doReturn(true).when(mockAc).finishActivity(any() /* token */, + anyInt() /* resultCode */, any() /* resultData */, anyInt() /* finishTask */); } private Activity launchActivity(ActivityClientRecord r) { diff --git a/core/tests/overlaytests/remount/TEST_MAPPING b/core/tests/overlaytests/remount/TEST_MAPPING index 54dd4310bd6e..22b28b5344bb 100644 --- a/core/tests/overlaytests/remount/TEST_MAPPING +++ b/core/tests/overlaytests/remount/TEST_MAPPING @@ -1,7 +1,7 @@ { - "presubmit": [ + "presubmit-large": [ { "name" : "OverlayRemountedTest" } ] -}
\ No newline at end of file +} diff --git a/core/tests/uwbtests/Android.bp b/core/tests/uwbtests/Android.bp index c41c346b131a..8ee86f470c9e 100644 --- a/core/tests/uwbtests/Android.bp +++ b/core/tests/uwbtests/Android.bp @@ -17,6 +17,7 @@ android_test { static_libs: [ "androidx.test.ext.junit", "androidx.test.rules", + "mockito-target-minus-junit4", ], libs: [ "android.test.runner", diff --git a/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java b/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java new file mode 100644 index 000000000000..ce67ef7868e8 --- /dev/null +++ b/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java @@ -0,0 +1,311 @@ +/* + * 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.uwb; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + + +import android.os.RemoteException; +import android.uwb.UwbManager.AdapterStateCallback; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Test of {@link AdapterStateListener}. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AdapterStateListenerTest { + + IUwbAdapter mUwbAdapter = mock(IUwbAdapter.class); + + Answer mRegisterSuccessAnswer = new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0]; + try { + cb.onAdapterStateChanged(false, StateChangeReason.UNKNOWN); + } catch (RemoteException e) { + // Nothing to do + } + return new Object(); + } + }; + + Throwable mThrowRemoteException = new RemoteException("RemoteException"); + + private static Executor getExecutor() { + return new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }; + } + + private static void verifyCallbackStateChangedInvoked( + AdapterStateCallback callback, int numTimes) { + verify(callback, times(numTimes)).onStateChanged(anyBoolean(), anyInt()); + } + + @Test + public void testRegister_RegisterUnregister() throws RemoteException { + doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback1 = mock(AdapterStateCallback.class); + AdapterStateCallback callback2 = mock(AdapterStateCallback.class); + + // Verify that the adapter state listener registered with the UWB Adapter + adapterStateListener.register(getExecutor(), callback1); + verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); + verifyCallbackStateChangedInvoked(callback1, 1); + verifyCallbackStateChangedInvoked(callback2, 0); + + // Register a second client and no new call to UWB Adapter + adapterStateListener.register(getExecutor(), callback2); + verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); + verifyCallbackStateChangedInvoked(callback1, 1); + verifyCallbackStateChangedInvoked(callback2, 1); + + // Unregister first callback + adapterStateListener.unregister(callback1); + verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); + verify(mUwbAdapter, times(0)).unregisterAdapterStateCallbacks(any()); + verifyCallbackStateChangedInvoked(callback1, 1); + verifyCallbackStateChangedInvoked(callback2, 1); + + // Unregister second callback + adapterStateListener.unregister(callback2); + verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); + verify(mUwbAdapter, times(1)).unregisterAdapterStateCallbacks(any()); + verifyCallbackStateChangedInvoked(callback1, 1); + verifyCallbackStateChangedInvoked(callback2, 1); + } + + @Test + public void testRegister_FirstRegisterFails() throws RemoteException { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback1 = mock(AdapterStateCallback.class); + AdapterStateCallback callback2 = mock(AdapterStateCallback.class); + + // Throw a remote exception whenever first registering + doThrow(mThrowRemoteException).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + adapterStateListener.register(getExecutor(), callback1); + verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); + + // No longer throw an exception, instead succeed + doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + // Register a different callback + adapterStateListener.register(getExecutor(), callback2); + verify(mUwbAdapter, times(2)).registerAdapterStateCallbacks(any()); + + // Ensure first callback was invoked again + verifyCallbackStateChangedInvoked(callback1, 2); + verifyCallbackStateChangedInvoked(callback2, 1); + } + + @Test + public void testRegister_RegisterSameCallbackTwice() throws RemoteException { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback = mock(AdapterStateCallback.class); + doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + adapterStateListener.register(getExecutor(), callback); + verifyCallbackStateChangedInvoked(callback, 1); + + adapterStateListener.register(getExecutor(), callback); + verifyCallbackStateChangedInvoked(callback, 1); + + // Invoke a state change and ensure the callback is only called once + adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN); + verifyCallbackStateChangedInvoked(callback, 2); + } + + @Test + public void testCallback_RunViaExecutor_Success() throws RemoteException { + // Verify that the callbacks are invoked on the executor when successful + doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + runViaExecutor(); + } + + @Test + public void testCallback_RunViaExecutor_Failure() throws RemoteException { + // Verify that the callbacks are invoked on the executor when there is a remote exception + doThrow(mThrowRemoteException).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + runViaExecutor(); + } + + private void runViaExecutor() { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback = mock(AdapterStateCallback.class); + + Executor executor = mock(Executor.class); + + // Do not run commands received and ensure that the callback is not invoked + doAnswer(new ExecutorAnswer(false)).when(executor).execute(any()); + adapterStateListener.register(executor, callback); + verify(executor, times(1)).execute(any()); + verifyCallbackStateChangedInvoked(callback, 0); + + // Manually invoke the callback and ensure callback is not invoked + adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN); + verify(executor, times(2)).execute(any()); + verifyCallbackStateChangedInvoked(callback, 0); + + // Run the command that the executor receives + doAnswer(new ExecutorAnswer(true)).when(executor).execute(any()); + adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN); + verify(executor, times(3)).execute(any()); + verifyCallbackStateChangedInvoked(callback, 1); + } + + class ExecutorAnswer implements Answer { + + final boolean mShouldRun; + ExecutorAnswer(boolean shouldRun) { + mShouldRun = shouldRun; + } + + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + if (mShouldRun) { + ((Runnable) invocation.getArgument(0)).run(); + } + return null; + } + } + + @Test + public void testNotify_AllCallbacksNotified() throws RemoteException { + doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + List<AdapterStateCallback> callbacks = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + AdapterStateCallback callback = mock(AdapterStateCallback.class); + adapterStateListener.register(getExecutor(), callback); + callbacks.add(callback); + } + + // Ensure every callback got the initial state + for (AdapterStateCallback callback : callbacks) { + verifyCallbackStateChangedInvoked(callback, 1); + } + + // Invoke a state change and ensure all callbacks are invoked + adapterStateListener.onAdapterStateChanged(true, StateChangeReason.ALL_SESSIONS_CLOSED); + for (AdapterStateCallback callback : callbacks) { + verifyCallbackStateChangedInvoked(callback, 2); + } + } + + @Test + public void testStateChange_CorrectValue() { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + + AdapterStateCallback callback = mock(AdapterStateCallback.class); + + adapterStateListener.register(getExecutor(), callback); + + runStateChangeValue(StateChangeReason.ALL_SESSIONS_CLOSED, + AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED); + + runStateChangeValue(StateChangeReason.SESSION_STARTED, + AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED); + + runStateChangeValue(StateChangeReason.SYSTEM_BOOT, + AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT); + + runStateChangeValue(StateChangeReason.SYSTEM_POLICY, + AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY); + + runStateChangeValue(StateChangeReason.UNKNOWN, + AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN); + } + + private void runStateChangeValue(@StateChangeReason int reasonIn, + @AdapterStateCallback.StateChangedReason int reasonOut) { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback = mock(AdapterStateCallback.class); + adapterStateListener.register(getExecutor(), callback); + + adapterStateListener.onAdapterStateChanged(false, reasonIn); + verify(callback, times(1)).onStateChanged(false, reasonOut); + + adapterStateListener.onAdapterStateChanged(true, reasonIn); + verify(callback, times(1)).onStateChanged(true, reasonOut); + } + + @Test + public void testStateChange_FirstRegisterGetsCorrectState() throws RemoteException { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback = mock(AdapterStateCallback.class); + + Answer registerAnswer = new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0]; + try { + cb.onAdapterStateChanged(true, StateChangeReason.SESSION_STARTED); + } catch (RemoteException e) { + // Nothing to do + } + return new Object(); + } + }; + + doAnswer(registerAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + adapterStateListener.register(getExecutor(), callback); + verify(callback).onStateChanged(true, + AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED); + } + + @Test + public void testStateChange_SecondRegisterGetsCorrectState() { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback1 = mock(AdapterStateCallback.class); + AdapterStateCallback callback2 = mock(AdapterStateCallback.class); + + adapterStateListener.register(getExecutor(), callback1); + adapterStateListener.onAdapterStateChanged(true, StateChangeReason.SYSTEM_BOOT); + + adapterStateListener.register(getExecutor(), callback2); + verify(callback2).onStateChanged(true, + AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT); + } +} diff --git a/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java b/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java new file mode 100644 index 000000000000..4983bed742fd --- /dev/null +++ b/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java @@ -0,0 +1,49 @@ +/* + * 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.uwb; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.content.Context; + +import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test of {@link UwbManager}. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class UwbManagerTest { + + public final Context mContext = InstrumentationRegistry.getContext(); + + @Test + public void testServiceAvailable() { + UwbManager manager = mContext.getSystemService(UwbManager.class); + if (UwbTestUtils.isUwbSupported(mContext)) { + assertNotNull(manager); + } else { + assertNull(manager); + } + } +} diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java index 62e0b629539b..fb7509248b38 100644 --- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java +++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java @@ -16,6 +16,8 @@ package android.uwb; +import android.content.Context; +import android.content.pm.PackageManager; import android.os.SystemClock; import java.util.ArrayList; @@ -24,6 +26,11 @@ import java.util.List; public class UwbTestUtils { private UwbTestUtils() {} + public static boolean isUwbSupported(Context context) { + PackageManager packageManager = context.getPackageManager(); + return packageManager.hasSystemFeature(PackageManager.FEATURE_UWB); + } + public static AngleMeasurement getAngleMeasurement() { return new AngleMeasurement.Builder() .setRadians(getDoubleInRange(-Math.PI, Math.PI)) diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 1fb63f04ccf5..5f129fe03b61 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -243,6 +243,8 @@ applications that come with the platform <permission name="android.permission.LOG_COMPAT_CHANGE" /> <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> <permission name="android.permission.REGISTER_STATS_PULL_ATOM" /> + <!-- Permissions required for reading DeviceConfig --> + <permission name="android.permission.READ_DEVICE_CONFIG" /> </privapp-permissions> <privapp-permissions package="com.android.providers.telephony"> @@ -375,6 +377,7 @@ applications that come with the platform <permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES"/> <permission name="android.permission.STATUS_BAR"/> <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" /> + <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" /> <permission name="android.permission.START_TASKS_FROM_RECENTS" /> <permission name="android.permission.STOP_APP_SWITCHES"/> <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/> @@ -445,6 +448,8 @@ applications that come with the platform <!-- Permissions required for CTS test - android.server.biometrics --> <permission name="android.permission.USE_BIOMETRIC" /> <permission name="android.permission.TEST_BIOMETRIC" /> + <!-- Permissions required for CTS test - CtsContactsProviderTestCases --> + <permission name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 6bcab8a34e2c..a5667b2f0a8a 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -85,6 +85,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "-2014162875": { + "message": "Could not register window container listener token=%s, container=%s", + "level": "ERROR", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowContextListenerController.java" + }, "-2012562539": { "message": "startAnimation(): Notify animation start:", "level": "DEBUG", @@ -799,12 +805,6 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, - "-1155279885": { - "message": "Frontmost changed immersion: %s", - "level": "DEBUG", - "group": "WM_DEBUG_IMMERSIVE", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, "-1144293044": { "message": "SURFACE SET FREEZE LAYER: %s", "level": "INFO", @@ -817,6 +817,12 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "-1136467585": { + "message": "The listener does not exist.", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/WindowContextListenerController.java" + }, "-1136139407": { "message": "no-history finish of %s", "level": "DEBUG", @@ -1219,6 +1225,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "-655104359": { + "message": "Frontmost changed immersion: %s", + "level": "DEBUG", + "group": "WM_DEBUG_IMMERSIVE", + "at": "com\/android\/server\/wm\/ActivityClientController.java" + }, "-653156702": { "message": "createAppAnimations()", "level": "DEBUG", @@ -1573,12 +1585,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/Task.java" }, - "-272719931": { - "message": "startLockTaskModeLocked: %s", - "level": "WARN", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, "-262984451": { "message": "Relaunch failed %s", "level": "INFO", @@ -1825,6 +1831,18 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, + "90764070": { + "message": "Could not report token removal to the window token client.", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowContextListenerController.java" + }, + "91350919": { + "message": "Attempted to set IME flag to a display that does not exist: %d", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "94402792": { "message": "Moving to RESUMED: %s (in existing)", "level": "VERBOSE", @@ -1969,6 +1987,12 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, + "236210101": { + "message": "registerWindowContextListener: trying to add listener to a non-existing display:%d", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "241961619": { "message": "Adding %s to %s", "level": "VERBOSE", @@ -2047,6 +2071,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, + "295861935": { + "message": "startLockTaskMode: %s", + "level": "WARN", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "302969511": { "message": "Task info changed taskId=%d", "level": "VERBOSE", @@ -2557,6 +2587,12 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/TaskDisplayArea.java" }, + "883475718": { + "message": "Report configuration: %s %s %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityClientController.java" + }, "892244061": { "message": "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d", "level": "INFO", @@ -3097,12 +3133,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "1576607724": { - "message": "Report configuration: %s %s %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, "1577579529": { "message": "win=%s destroySurfaces: appStopped=%b win.mWindowRemovalAllowed=%b win.mRemoveOnExit=%b", "level": "ERROR", @@ -3403,6 +3433,12 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "1948483534": { + "message": "Could not report config changes to the window token client.", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowContextListenerController.java" + }, "1964565370": { "message": "Starting remote animation", "level": "INFO", diff --git a/errorprone/refaster/EfficientXml.java b/errorprone/refaster/EfficientXml.java index bd1ddfc92e91..ae797c46b77e 100644 --- a/errorprone/refaster/EfficientXml.java +++ b/errorprone/refaster/EfficientXml.java @@ -292,6 +292,30 @@ public class EfficientXml { } } + class BooleanToStringTrue { + @BeforeTemplate + void before(TypedXmlSerializer out, String n) throws Exception { + out.attribute(null, n, "true"); + } + + @AfterTemplate + void after(TypedXmlSerializer out, String n) throws Exception { + out.attributeBoolean(null, n, true); + } + } + + class BooleanToStringFalse { + @BeforeTemplate + void before(TypedXmlSerializer out, String n) throws Exception { + out.attribute(null, n, "false"); + } + + @AfterTemplate + void after(TypedXmlSerializer out, String n) throws Exception { + out.attributeBoolean(null, n, false); + } + } + class BooleanFromString { @BeforeTemplate boolean beforeParse(TypedXmlPullParser in, String n) throws Exception { diff --git a/errorprone/refaster/EfficientXml.java.refaster b/errorprone/refaster/EfficientXml.java.refaster Binary files differindex f2974fffb83b..750c2dbe98c0 100644 --- a/errorprone/refaster/EfficientXml.java.refaster +++ b/errorprone/refaster/EfficientXml.java.refaster diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java index 0061ea13f647..d59abb5916a0 100644 --- a/graphics/java/android/graphics/FrameInfo.java +++ b/graphics/java/android/graphics/FrameInfo.java @@ -43,7 +43,7 @@ public final class FrameInfo { public long[] frameInfo = new long[FRAME_INFO_SIZE]; // Various flags set to provide extra metadata about the current frame - private static final int FLAGS = 0; + public static final int FLAGS = 0; // Is this the first-draw following a window layout? public static final long FLAG_WINDOW_LAYOUT_CHANGED = 1; @@ -60,35 +60,35 @@ public final class FrameInfo { @Retention(RetentionPolicy.SOURCE) public @interface FrameInfoFlags {} - private static final int FRAME_TIMELINE_VSYNC_ID = 1; + public static final int FRAME_TIMELINE_VSYNC_ID = 1; // The intended vsync time, unadjusted by jitter - private static final int INTENDED_VSYNC = 2; + public static final int INTENDED_VSYNC = 2; // Jitter-adjusted vsync time, this is what was used as input into the // animation & drawing system - private static final int VSYNC = 3; + public static final int VSYNC = 3; // The time of the oldest input event - private static final int OLDEST_INPUT_EVENT = 4; + public static final int OLDEST_INPUT_EVENT = 4; // The time of the newest input event - private static final int NEWEST_INPUT_EVENT = 5; + public static final int NEWEST_INPUT_EVENT = 5; // When input event handling started - private static final int HANDLE_INPUT_START = 6; + public static final int HANDLE_INPUT_START = 6; // When animation evaluations started - private static final int ANIMATION_START = 7; + public static final int ANIMATION_START = 7; // When ViewRootImpl#performTraversals() started - private static final int PERFORM_TRAVERSALS_START = 8; + public static final int PERFORM_TRAVERSALS_START = 8; // When View:draw() started - private static final int DRAW_START = 9; + public static final int DRAW_START = 9; // When the frame needs to be ready by - private static final int FRAME_DEADLINE = 10; + public static final int FRAME_DEADLINE = 10; // Must be the last one private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1; @@ -99,23 +99,11 @@ public final class FrameInfo { frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId; frameInfo[INTENDED_VSYNC] = intendedVsync; frameInfo[VSYNC] = usedVsync; - frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE; - frameInfo[NEWEST_INPUT_EVENT] = 0; frameInfo[FLAGS] = 0; frameInfo[FRAME_DEADLINE] = frameDeadline; } /** checkstyle */ - public void updateInputEventTime(long inputEventTime, long inputEventOldestTime) { - if (inputEventOldestTime < frameInfo[OLDEST_INPUT_EVENT]) { - frameInfo[OLDEST_INPUT_EVENT] = inputEventOldestTime; - } - if (inputEventTime > frameInfo[NEWEST_INPUT_EVENT]) { - frameInfo[NEWEST_INPUT_EVENT] = inputEventTime; - } - } - - /** checkstyle */ public void markInputHandlingStart() { frameInfo[HANDLE_INPUT_START] = System.nanoTime(); } @@ -131,13 +119,7 @@ public final class FrameInfo { } /** checkstyle */ - public void markDrawStart() { - frameInfo[DRAW_START] = System.nanoTime(); - } - - /** checkstyle */ public void addFlags(@FrameInfoFlags long flags) { frameInfo[FLAGS] |= flags; } - } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 24987daf87b5..d6b4f1824617 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -82,7 +82,7 @@ public class Typeface { private static String TAG = "Typeface"; /** @hide */ - public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = false; + public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = true; private static final NativeAllocationRegistry sRegistry = NativeAllocationRegistry.createMalloced( diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index e7c15821e5b3..fea756c8290f 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -124,6 +124,19 @@ public final class Font { } /** + * Construct a builder with a byte buffer and file path. + * + * This method is intended to be called only from SystemFonts. + * @param path font file path + * @param localeList comma concatenated BCP47 compliant language tag. + * @hide + */ + public Builder(@NonNull File path, @NonNull String localeList) { + this(path); + mLocaleList = localeList; + } + + /** * Constructs a builder with a file path. * * @param path a file path to the font file @@ -809,29 +822,13 @@ public final class Font { // If not found, create Font object from native object for Java API users. ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr); - long packed = nGetFontInfo(ptr); - int weight = (int) (packed & 0x0000_0000_0000_FFFFL); - boolean italic = (packed & 0x0000_0000_0001_0000L) != 0; - int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32); - int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48); - FontVariationAxis[] axes = new FontVariationAxis[axisCount]; - char[] charBuffer = new char[4]; - for (int i = 0; i < axisCount; ++i) { - long packedAxis = nGetAxisInfo(ptr, i); - float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL)); - charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56); - charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48); - charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40); - charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32); - axes[i] = new FontVariationAxis(new String(charBuffer), value); - } - String path = nGetFontPath(ptr); - File file = (path == null) ? null : new File(path); - Font.Builder builder = new Font.Builder(buffer, file, "") - .setWeight(weight) - .setSlant(italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT) - .setTtcIndex(ttcIndex) - .setFontVariationSettings(axes); + NativeFont.Font font = NativeFont.readNativeFont(ptr); + + Font.Builder builder = new Font.Builder(buffer, font.getFile(), "") + .setWeight(font.getStyle().getWeight()) + .setSlant(font.getStyle().getSlant()) + .setTtcIndex(font.getIndex()) + .setFontVariationSettings(font.getAxes()); Font newFont = null; try { @@ -845,15 +842,6 @@ public final class Font { } } - @CriticalNative - private static native long nGetFontInfo(long ptr); - - @CriticalNative - private static native long nGetAxisInfo(long ptr, int i); - - @FastNative - private static native String nGetFontPath(long ptr); - @FastNative private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect); diff --git a/graphics/java/android/graphics/fonts/NativeFont.java b/graphics/java/android/graphics/fonts/NativeFont.java new file mode 100644 index 000000000000..9e9d76a89385 --- /dev/null +++ b/graphics/java/android/graphics/fonts/NativeFont.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.graphics.fonts; + +import android.graphics.Typeface; + +import dalvik.annotation.optimization.CriticalNative; +import dalvik.annotation.optimization.FastNative; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * Read native font objects. + * + * @hide + */ +public class NativeFont { + + /** + * Represents native font object. + */ + public static final class Font { + private final File mFile; + private final int mIndex; + private final FontVariationAxis[] mAxes; + private final FontStyle mStyle; + + public Font(File file, int index, FontVariationAxis[] axes, FontStyle style) { + mFile = file; + mIndex = index; + mAxes = axes; + mStyle = style; + } + + public File getFile() { + return mFile; + } + + public FontVariationAxis[] getAxes() { + return mAxes; + } + + public FontStyle getStyle() { + return mStyle; + } + + public int getIndex() { + return mIndex; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Font font = (Font) o; + return mIndex == font.mIndex && mFile.equals(font.mFile) + && Arrays.equals(mAxes, font.mAxes) && mStyle.equals(font.mStyle); + } + + @Override + public int hashCode() { + int result = Objects.hash(mFile, mIndex, mStyle); + result = 31 * result + Arrays.hashCode(mAxes); + return result; + } + } + + /** + * Represents native font family object. + */ + public static final class Family { + private final List<Font> mFonts; + private final String mLocale; + + public Family(List<Font> fonts, String locale) { + mFonts = fonts; + mLocale = locale; + } + + public List<Font> getFonts() { + return mFonts; + } + + public String getLocale() { + return mLocale; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Family family = (Family) o; + return mFonts.equals(family.mFonts) && mLocale.equals(family.mLocale); + } + + @Override + public int hashCode() { + return Objects.hash(mFonts, mLocale); + } + } + + /** + * Get underlying font families from Typeface + * + * @param typeface a typeface + * @return list of family + */ + public static List<Family> readTypeface(Typeface typeface) { + int familyCount = nGetFamilyCount(typeface.native_instance); + List<Family> result = new ArrayList<>(familyCount); + for (int i = 0; i < familyCount; ++i) { + result.add(readNativeFamily(nGetFamily(typeface.native_instance, i))); + } + return result; + } + + /** + * Read family object from native pointer + * + * @param familyPtr a font family pointer + * @return a family + */ + public static Family readNativeFamily(long familyPtr) { + int fontCount = nGetFontCount(familyPtr); + List<Font> result = new ArrayList<>(fontCount); + for (int i = 0; i < fontCount; ++i) { + result.add(readNativeFont(nGetFont(familyPtr, i))); + } + String localeList = nGetLocaleList(familyPtr); + return new Family(result, localeList); + } + + /** + * Read font object from native pointer. + * + * @param ptr a font pointer + * @return a font + */ + public static Font readNativeFont(long ptr) { + long packed = nGetFontInfo(ptr); + int weight = (int) (packed & 0x0000_0000_0000_FFFFL); + boolean italic = (packed & 0x0000_0000_0001_0000L) != 0; + int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32); + int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48); + FontVariationAxis[] axes = new FontVariationAxis[axisCount]; + char[] charBuffer = new char[4]; + for (int i = 0; i < axisCount; ++i) { + long packedAxis = nGetAxisInfo(ptr, i); + float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL)); + charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56); + charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48); + charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40); + charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32); + axes[i] = new FontVariationAxis(new String(charBuffer), value); + } + String path = nGetFontPath(ptr); + File file = (path == null) ? null : new File(path); + FontStyle style = new FontStyle(weight, + italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT); + + return new Font(file, ttcIndex, axes, style); + } + + @CriticalNative + private static native int nGetFamilyCount(long ptr); + + @CriticalNative + private static native long nGetFamily(long ptr, int index); + + @FastNative + private static native String nGetLocaleList(long familyPtr); + + @CriticalNative + private static native long nGetFont(long familyPtr, int fontIndex); + + @CriticalNative + private static native int nGetFontCount(long familyPtr); + + @CriticalNative + private static native long nGetFontInfo(long fontPtr); + + @CriticalNative + private static native long nGetAxisInfo(long fontPtr, int i); + + @FastNative + private static native String nGetFontPath(long fontPtr); +} diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 3635adc3ce39..fb6ea99be7ab 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -19,6 +19,7 @@ package android.graphics.fonts; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.FontListParser; +import android.graphics.Typeface; import android.text.FontConfig; import android.util.ArrayMap; import android.util.Log; @@ -68,21 +69,40 @@ public final class SystemFonts { return sAvailableFonts; } - Set<Font> set = new HashSet<>(); - - for (FontFamily[] items : sFamilyMap.values()) { - for (FontFamily family : items) { - for (int i = 0; i < family.getSize(); ++i) { - set.add(family.getFont(i)); + if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + sAvailableFonts = collectAllFonts(); + } else { + Set<Font> set = new HashSet<>(); + for (FontFamily[] items : sFamilyMap.values()) { + for (FontFamily family : items) { + for (int i = 0; i < family.getSize(); ++i) { + set.add(family.getFont(i)); + } } } - } - sAvailableFonts = Collections.unmodifiableSet(set); + sAvailableFonts = Collections.unmodifiableSet(set); + } return sAvailableFonts; } } + private static @NonNull Set<Font> collectAllFonts() { + final FontCustomizationParser.Result oemCustomization = + readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); + Map<String, FontFamily[]> map = new ArrayMap<>(); + buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", oemCustomization, map); + Set<Font> res = new HashSet<>(); + for (FontFamily[] families : map.values()) { + for (FontFamily family : families) { + for (int i = 0; i < family.getSize(); ++i) { + res.add(family.getFont(i)); + } + } + } + return res; + } + private static @Nullable ByteBuffer mmap(@NonNull String fullPath) { try (FileInputStream file = new FileInputStream(fullPath)) { final FileChannel fileChannel = file.getChannel(); diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 39e32c694d2e..96e0559b0df6 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -122,12 +122,14 @@ android_library { "kotlinx-coroutines-android", "kotlinx-coroutines-core", "iconloader_base", + "jsr330", "protolog-lib", "SettingsLib", "WindowManager-Shell-proto", + "jsr330" ], kotlincflags: ["-Xjvm-default=enable"], manifest: "AndroidManifest.xml", min_sdk_version: "26", -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml index b86f36a14d17..341fe617b2d0 100644 --- a/libs/WindowManager/Shell/res/layout/split_divider.xml +++ b/libs/WindowManager/Shell/res/layout/split_divider.xml @@ -14,7 +14,7 @@ ~ limitations under the License. --> -<com.android.wm.shell.apppairs.DividerView +<com.android.wm.shell.common.split.DividerView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="match_parent" android:layout_width="match_parent"> @@ -24,4 +24,4 @@ android:id="@+id/docked_divider_background" android:background="@color/docked_divider_background"/> -</com.android.wm.shell.apppairs.DividerView> +</com.android.wm.shell.common.split.DividerView> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java index ada7e1a9e3ab..45948dd9e800 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java @@ -19,6 +19,7 @@ package com.android.wm.shell; import android.view.Gravity; import com.android.wm.shell.apppairs.AppPairs; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.letterbox.LetterboxConfigController; import com.android.wm.shell.onehanded.OneHanded; @@ -61,6 +62,7 @@ public final class ShellCommandHandler { } /** Dumps WM Shell internal state. */ + @ExternalThread public void dump(PrintWriter pw) { mShellTaskOrganizer.dump(pw, ""); pw.println(); @@ -76,6 +78,7 @@ public final class ShellCommandHandler { /** Returns {@code true} if command was found and executed. */ + @ExternalThread public boolean handleCommand(String[] args, PrintWriter pw) { if (args.length < 2) { // Argument at position 0 is "WMShell". diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java index 118f189866eb..94555de4f05c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java @@ -21,6 +21,7 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_LETTERB import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.letterbox.LetterboxTaskListener; import com.android.wm.shell.splitscreen.SplitScreen; @@ -56,6 +57,7 @@ public class ShellInit { mFullscreenTaskListener = fullscreenTaskListener; } + @ExternalThread public void init() { // Start listening for display changes mDisplayImeController.startMonitorDisplays(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index beb9690b5cbc..174c16afc75f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -31,12 +31,14 @@ import android.os.Binder; import android.os.IBinder; import android.util.ArrayMap; import android.util.Log; +import android.util.Slog; import android.util.SparseArray; import android.view.SurfaceControl; import android.window.ITaskOrganizerController; import android.window.TaskAppearedInfo; import android.window.TaskOrganizer; +import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -45,6 +47,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.startingsurface.StartingSurfaceDrawer; import java.io.PrintWriter; @@ -119,7 +122,7 @@ public class ShellTaskOrganizer extends TaskOrganizer { ShellExecutor mainExecutor, ShellExecutor animExecutor, Context context) { super(taskOrganizerController, mainExecutor); mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor); - if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions); + if (Transitions.ENABLE_SHELL_TRANSITIONS) mTransitions.register(this); // TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled // by a controller, that class should be create while porting // ActivityRecord#addStartingWindow to WMShell. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java index 120039de1240..10195b6a26b2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java @@ -23,9 +23,9 @@ import static android.window.TransitionInfo.TRANSIT_SHOW; import android.animation.Animator; import android.animation.ValueAnimator; -import android.annotation.MainThread; import android.annotation.NonNull; import android.os.IBinder; +import android.os.RemoteException; import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Slog; @@ -35,15 +35,18 @@ import android.window.ITransitionPlayer; import android.window.TransitionInfo; import android.window.WindowOrganizer; +import androidx.annotation.BinderThread; + import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.ArrayList; /** Plays transition animations */ -public class Transitions extends ITransitionPlayer.Stub { +public class Transitions { private static final String TAG = "ShellTransitions"; /** Set to {@code true} to enable shell transitions. */ @@ -54,6 +57,7 @@ public class Transitions extends ITransitionPlayer.Stub { private final TransactionPool mTransactionPool; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; + private final TransitionPlayerImpl mPlayerImpl; /** Keeps track of currently tracked transitions and all the animations associated with each */ private final ArrayMap<IBinder, ArrayList<Animator>> mActiveTransitions = new ArrayMap<>(); @@ -64,6 +68,11 @@ public class Transitions extends ITransitionPlayer.Stub { mTransactionPool = pool; mMainExecutor = mainExecutor; mAnimExecutor = animExecutor; + mPlayerImpl = new TransitionPlayerImpl(); + } + + public void register(ShellTaskOrganizer taskOrganizer) { + taskOrganizer.registerTransitionPlayer(mPlayerImpl); } // TODO(shell-transitions): real animations @@ -115,77 +124,73 @@ public class Transitions extends ITransitionPlayer.Stub { || type == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; } - @Override - public void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, + private void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", transitionToken, info); // start task - mMainExecutor.execute(() -> { - if (!mActiveTransitions.containsKey(transitionToken)) { - Slog.e(TAG, "Got transitionReady for non-active transition " + transitionToken - + " expecting one of " + mActiveTransitions.keySet()); - } - if (mActiveTransitions.get(transitionToken) != null) { - throw new IllegalStateException("Got a duplicate onTransitionReady call for " - + transitionToken); - } - mActiveTransitions.put(transitionToken, new ArrayList<>()); - boolean isOpening = isOpeningType(info.getType()); - if (info.getRootLeash().isValid()) { - t.show(info.getRootLeash()); - } - // changes should be ordered top-to-bottom in z - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final SurfaceControl leash = change.getLeash(); - final int mode = info.getChanges().get(i).getMode(); - - // Don't animate anything with an animating parent - if (change.getParent() != null) { - if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { - t.show(leash); - t.setMatrix(leash, 1, 0, 0, 1); - } - continue; - } - - t.reparent(leash, info.getRootLeash()); - t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x, - change.getEndAbsBounds().top - info.getRootOffset().y); - // Put all the OPEN/SHOW on top + if (!mActiveTransitions.containsKey(transitionToken)) { + Slog.e(TAG, "Got transitionReady for non-active transition " + transitionToken + + " expecting one of " + mActiveTransitions.keySet()); + } + if (mActiveTransitions.get(transitionToken) != null) { + throw new IllegalStateException("Got a duplicate onTransitionReady call for " + + transitionToken); + } + mActiveTransitions.put(transitionToken, new ArrayList<>()); + boolean isOpening = isOpeningType(info.getType()); + if (info.getRootLeash().isValid()) { + t.show(info.getRootLeash()); + } + // changes should be ordered top-to-bottom in z + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + final SurfaceControl leash = change.getLeash(); + final int mode = info.getChanges().get(i).getMode(); + + // Don't animate anything with an animating parent + if (change.getParent() != null) { if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { t.show(leash); t.setMatrix(leash, 1, 0, 0, 1); - if (isOpening) { - // put on top and fade in - t.setLayer(leash, info.getChanges().size() - i); - t.setAlpha(leash, 0.f); - startExampleAnimation(transitionToken, leash, true /* show */); - } else { - // put on bottom and leave it visible without fade - t.setLayer(leash, -i); - t.setAlpha(leash, 1.f); - } - } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) { - if (isOpening) { - // put on bottom and leave visible without fade - t.setLayer(leash, -i); - } else { - // put on top and fade out - t.setLayer(leash, info.getChanges().size() - i); - startExampleAnimation(transitionToken, leash, false /* show */); - } + } + continue; + } + + t.reparent(leash, info.getRootLeash()); + t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x, + change.getEndAbsBounds().top - info.getRootOffset().y); + // Put all the OPEN/SHOW on top + if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { + t.show(leash); + t.setMatrix(leash, 1, 0, 0, 1); + if (isOpening) { + // put on top and fade in + t.setLayer(leash, info.getChanges().size() - i); + t.setAlpha(leash, 0.f); + startExampleAnimation(transitionToken, leash, true /* show */); } else { + // put on bottom and leave it visible without fade + t.setLayer(leash, -i); + t.setAlpha(leash, 1.f); + } + } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) { + if (isOpening) { + // put on bottom and leave visible without fade + t.setLayer(leash, -i); + } else { + // put on top and fade out t.setLayer(leash, info.getChanges().size() - i); + startExampleAnimation(transitionToken, leash, false /* show */); } + } else { + t.setLayer(leash, info.getChanges().size() - i); } - t.apply(); - onFinish(transitionToken); - }); + } + t.apply(); + onFinish(transitionToken); } - @MainThread private void onFinish(IBinder transition) { if (!mActiveTransitions.get(transition).isEmpty()) return; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, @@ -194,16 +199,32 @@ public class Transitions extends ITransitionPlayer.Stub { mOrganizer.finishTransition(transition, null, null); } - @Override - public void requestStartTransition(int type, @NonNull IBinder transitionToken) { + private void requestStartTransition(int type, @NonNull IBinder transitionToken) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: type=%d %s", type, transitionToken); - mMainExecutor.execute(() -> { - if (mActiveTransitions.containsKey(transitionToken)) { - throw new RuntimeException("Transition already started " + transitionToken); - } - IBinder transition = mOrganizer.startTransition(type, transitionToken, null /* wct */); - mActiveTransitions.put(transition, null); - }); + + if (mActiveTransitions.containsKey(transitionToken)) { + throw new RuntimeException("Transition already started " + transitionToken); + } + IBinder transition = mOrganizer.startTransition(type, transitionToken, null /* wct */); + mActiveTransitions.put(transition, null); + } + + @BinderThread + private class TransitionPlayerImpl extends ITransitionPlayer.Stub { + @Override + public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo, + SurfaceControl.Transaction transaction) throws RemoteException { + mMainExecutor.execute(() -> { + Transitions.this.onTransitionReady(iBinder, transitionInfo, transaction); + }); + } + + @Override + public void requestStartTransition(int i, IBinder iBinder) throws RemoteException { + mMainExecutor.execute(() -> { + Transitions.this.requestStartTransition(i, iBinder); + }); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java index acb9a5dae78c..834de3f15b1d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java @@ -18,11 +18,11 @@ package com.android.wm.shell; import static android.view.Display.DEFAULT_DISPLAY; -import android.app.WindowConfiguration; import android.os.RemoteException; -import android.view.WindowManagerGlobal; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PinnedStackListenerForwarder; +import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedStackListener; /** * The singleton wrapper to communicate between WindowManagerService and WMShell features @@ -31,32 +31,30 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder; public class WindowManagerShellWrapper { private static final String TAG = WindowManagerShellWrapper.class.getSimpleName(); - public static final int WINDOWING_MODE_PINNED = WindowConfiguration.WINDOWING_MODE_PINNED; - /** * Forwarder to which we can add multiple pinned stack listeners. Each listener will receive * updates from the window manager service. */ - private PinnedStackListenerForwarder mPinnedStackListenerForwarder = - new PinnedStackListenerForwarder(); + private final PinnedStackListenerForwarder mPinnedStackListenerForwarder; + + public WindowManagerShellWrapper(ShellExecutor shellMainExecutor) { + mPinnedStackListenerForwarder = new PinnedStackListenerForwarder(shellMainExecutor); + } /** * Adds a pinned stack listener, which will receive updates from the window manager service * along with any other pinned stack listeners that were added via this method. */ - public void addPinnedStackListener(PinnedStackListenerForwarder.PinnedStackListener listener) - throws - RemoteException { + public void addPinnedStackListener(PinnedStackListener listener) + throws RemoteException { mPinnedStackListenerForwarder.addListener(listener); - WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener( - DEFAULT_DISPLAY, mPinnedStackListenerForwarder); + mPinnedStackListenerForwarder.register(DEFAULT_DISPLAY); } /** * Removes a pinned stack listener. */ - public void removePinnedStackListener( - PinnedStackListenerForwarder.PinnedStackListener listener) { + public void removePinnedStackListener(PinnedStackListener listener) { mPinnedStackListenerForwarder.removeListener(listener); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java index 357f777e1270..176c620fa119 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java @@ -23,6 +23,8 @@ import android.view.ViewPropertyAnimator; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import javax.inject.Inject; + /** * Utility class to calculate general fling animation when the finger is released. */ @@ -368,6 +370,7 @@ public class FlingAnimationUtils { float mX2; float mY2; + @Inject public Builder(DisplayMetrics displayMetrics) { mDisplayMetrics = displayMetrics; reset(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index d3032f83fc1c..cfbf8452ddae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -29,11 +29,13 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.split.SplitLayout; import java.io.PrintWriter; @@ -42,7 +44,7 @@ import java.io.PrintWriter; * {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair. * Also includes all UI for managing the pair like the divider. */ -class AppPair implements ShellTaskOrganizer.TaskListener { +class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChangeListener { private static final String TAG = AppPair.class.getSimpleName(); private ActivityManager.RunningTaskInfo mRootTaskInfo; @@ -55,7 +57,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener { private final AppPairsController mController; private final SyncTransactionQueue mSyncQueue; private final DisplayController mDisplayController; - private AppPairLayout mAppPairLayout; + private SplitLayout mSplitLayout; AppPair(AppPairsController controller) { mController = controller; @@ -92,11 +94,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener { mTaskInfo1 = task1; mTaskInfo2 = task2; - mAppPairLayout = new AppPairLayout( + mSplitLayout = new SplitLayout( mDisplayController.getDisplayContext(mRootTaskInfo.displayId), - mDisplayController.getDisplay(mRootTaskInfo.displayId), - mRootTaskInfo.configuration, - mRootTaskLeash); + mRootTaskInfo.configuration, this, mRootTaskLeash); final WindowContainerToken token1 = task1.token; final WindowContainerToken token2 = task2.token; @@ -107,8 +107,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener { .reparent(token2, mRootTaskInfo.token, true /* onTop */) .setWindowingMode(token1, WINDOWING_MODE_MULTI_WINDOW) .setWindowingMode(token2, WINDOWING_MODE_MULTI_WINDOW) - .setBounds(token1, mAppPairLayout.getBounds1()) - .setBounds(token2, mAppPairLayout.getBounds2()) + .setBounds(token1, mSplitLayout.getBounds1()) + .setBounds(token2, mSplitLayout.getBounds2()) // Moving the root task to top after the child tasks were repareted , or the root // task cannot be visible and focused. .reorder(mRootTaskInfo.token, true); @@ -117,6 +117,10 @@ class AppPair implements ShellTaskOrganizer.TaskListener { } void unpair() { + unpair(null /* toTopToken */); + } + + private void unpair(@Nullable WindowContainerToken toTopToken) { final WindowContainerToken token1 = mTaskInfo1.token; final WindowContainerToken token2 = mTaskInfo2.token; final WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -124,16 +128,16 @@ class AppPair implements ShellTaskOrganizer.TaskListener { // Reparent out of this container and reset windowing mode. wct.setHidden(mRootTaskInfo.token, true) .reorder(mRootTaskInfo.token, false) - .reparent(token1, null, false /* onTop */) - .reparent(token2, null, false /* onTop */) + .reparent(token1, null, token1 == toTopToken /* onTop */) + .reparent(token2, null, token2 == toTopToken /* onTop */) .setWindowingMode(token1, WINDOWING_MODE_UNDEFINED) .setWindowingMode(token2, WINDOWING_MODE_UNDEFINED); mController.getTaskOrganizer().applyTransaction(wct); mTaskInfo1 = null; mTaskInfo2 = null; - mAppPairLayout.release(); - mAppPairLayout = null; + mSplitLayout.release(); + mSplitLayout = null; } @Override @@ -153,17 +157,17 @@ class AppPair implements ShellTaskOrganizer.TaskListener { if (mTaskLeash1 == null || mTaskLeash2 == null) return; - mAppPairLayout.init(); - final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash(); - final Rect dividerBounds = mAppPairLayout.getDividerBounds(); + mSplitLayout.init(); + final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); + final Rect dividerBounds = mSplitLayout.getDividerBounds(); // TODO: Is there more we need to do here? mSyncQueue.runInSync(t -> { - t.setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x, - mTaskInfo1.positionInParent.y) + t.setLayer(dividerLeash, Integer.MAX_VALUE) + .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x, + mTaskInfo1.positionInParent.y) .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x, mTaskInfo2.positionInParent.y) - .setLayer(dividerLeash, Integer.MAX_VALUE) .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) .show(mRootTaskLeash) .show(mTaskLeash1) @@ -185,14 +189,14 @@ class AppPair implements ShellTaskOrganizer.TaskListener { } mRootTaskInfo = taskInfo; - if (mAppPairLayout != null - && mAppPairLayout.updateConfiguration(mRootTaskInfo.configuration)) { + if (mSplitLayout != null + && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) { // Update bounds when root bounds or its orientation changed. final WindowContainerTransaction wct = new WindowContainerTransaction(); - final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash(); - final Rect dividerBounds = mAppPairLayout.getDividerBounds(); - final Rect bounds1 = mAppPairLayout.getBounds1(); - final Rect bounds2 = mAppPairLayout.getBounds2(); + final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); + final Rect dividerBounds = mSplitLayout.getDividerBounds(); + final Rect bounds1 = mSplitLayout.getBounds1(); + final Rect bounds2 = mSplitLayout.getBounds2(); wct.setBounds(mTaskInfo1.token, bounds1) .setBounds(mTaskInfo2.token, bounds2); @@ -200,7 +204,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener { mSyncQueue.runInSync(t -> t .setPosition(mTaskLeash1, bounds1.left, bounds1.top) .setPosition(mTaskLeash2, bounds2.left, bounds2.top) - .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)); + .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) + // Resets layer to divider bar to make sure it is always on top. + .setLayer(dividerLeash, Integer.MAX_VALUE)); } } else if (taskInfo.taskId == getTaskId1()) { mTaskInfo1 = taskInfo; @@ -242,4 +248,39 @@ class AppPair implements ShellTaskOrganizer.TaskListener { public String toString() { return TAG + "#" + getRootTaskId(); } + + @Override + public void onSnappedToDismiss(boolean snappedToEnd) { + unpair(snappedToEnd ? mTaskInfo1.token : mTaskInfo2.token /* toTopToken */); + } + + @Override + public void onBoundsChanging(SplitLayout layout) { + final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); + if (dividerLeash == null) return; + final Rect dividerBounds = layout.getDividerBounds(); + final Rect bounds1 = layout.getBounds1(); + final Rect bounds2 = layout.getBounds2(); + mSyncQueue.runInSync(t -> t + .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) + .setPosition(mTaskLeash1, bounds1.left, bounds1.top) + .setPosition(mTaskLeash2, bounds2.left, bounds2.top)); + } + + @Override + public void onBoundsChanged(SplitLayout layout) { + final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); + if (dividerLeash == null) return; + final Rect dividerBounds = layout.getDividerBounds(); + final Rect bounds1 = layout.getBounds1(); + final Rect bounds2 = layout.getBounds2(); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mTaskInfo1.token, bounds1) + .setBounds(mTaskInfo2.token, bounds2); + mController.getTaskOrganizer().applyTransaction(wct); + mSyncQueue.runInSync(t -> t + .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) + .setPosition(mTaskLeash1, bounds1.left, bounds1.top) + .setPosition(mTaskLeash2, bounds2.left, bounds2.top)); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java deleted file mode 100644 index 8c8655e1ff1f..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java +++ /dev/null @@ -1,211 +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.wm.shell.apppairs; - -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; -import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; -import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; -import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; - -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.Region; -import android.os.Binder; -import android.os.IBinder; -import android.view.Display; -import android.view.IWindow; -import android.view.LayoutInflater; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.WindowManager; -import android.view.WindowlessWindowManager; - -import com.android.wm.shell.R; - -/** - * Records and handles layout of a pair of apps. - */ -final class AppPairLayout { - private static final String DIVIDER_WINDOW_TITLE = "AppPairDivider"; - private final Display mDisplay; - private final int mDividerWindowWidth; - private final int mDividerWindowInsets; - private final AppPairWindowManager mAppPairWindowManager; - - private Context mContext; - private Rect mRootBounds; - private DIVIDE_POLICY mDividePolicy; - - private SurfaceControlViewHost mViewHost; - private SurfaceControl mDividerLeash; - - AppPairLayout( - Context context, - Display display, - Configuration configuration, - SurfaceControl rootLeash) { - mContext = context.createConfigurationContext(configuration); - mDisplay = display; - mRootBounds = configuration.windowConfiguration.getBounds(); - mDividerWindowWidth = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_thickness); - mDividerWindowInsets = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_insets); - - mAppPairWindowManager = new AppPairWindowManager(configuration, rootLeash); - mDividePolicy = DIVIDE_POLICY.MIDDLE; - mDividePolicy.update(mRootBounds, mDividerWindowWidth, mDividerWindowInsets); - } - - boolean updateConfiguration(Configuration configuration) { - mAppPairWindowManager.setConfiguration(configuration); - final Rect rootBounds = configuration.windowConfiguration.getBounds(); - if (isIdenticalBounds(mRootBounds, rootBounds)) { - return false; - } - - mContext = mContext.createConfigurationContext(configuration); - mRootBounds = rootBounds; - mDividePolicy.update(mRootBounds, mDividerWindowWidth, mDividerWindowInsets); - release(); - init(); - return true; - } - - Rect getBounds1() { - return mDividePolicy.mBounds1; - } - - Rect getBounds2() { - return mDividePolicy.mBounds2; - } - - Rect getDividerBounds() { - return mDividePolicy.mDividerBounds; - } - - SurfaceControl getDividerLeash() { - return mDividerLeash; - } - - void release() { - if (mViewHost == null) { - return; - } - mViewHost.release(); - mDividerLeash = null; - mViewHost = null; - } - - void init() { - if (mViewHost == null) { - mViewHost = new SurfaceControlViewHost(mContext, mDisplay, mAppPairWindowManager); - } - - final DividerView dividerView = (DividerView) LayoutInflater.from(mContext) - .inflate(R.layout.split_divider, null); - - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - mDividePolicy.mDividerBounds.width(), - mDividePolicy.mDividerBounds.height(), - TYPE_DOCK_DIVIDER, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH - | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY, - PixelFormat.TRANSLUCENT); - lp.token = new Binder(); - lp.setTitle(DIVIDER_WINDOW_TITLE); - lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; - - mViewHost.setView(dividerView, lp); - mDividerLeash = mAppPairWindowManager.getSurfaceControl(mViewHost.getWindowToken()); - } - - private static boolean isIdenticalBounds(Rect bounds1, Rect bounds2) { - return bounds1.left == bounds2.left && bounds1.top == bounds2.top - && bounds1.right == bounds2.right && bounds1.bottom == bounds2.bottom; - } - - /** - * Indicates the policy of placing divider bar and corresponding split-screens. - */ - // TODO(172704238): add more divide policy and provide snap to resize feature for divider bar. - enum DIVIDE_POLICY { - MIDDLE; - - void update(Rect rootBounds, int dividerWindowWidth, int dividerWindowInsets) { - final int dividerOffset = dividerWindowWidth / 2; - final int boundsOffset = dividerOffset - dividerWindowInsets; - - mDividerBounds = new Rect(rootBounds); - mBounds1 = new Rect(rootBounds); - mBounds2 = new Rect(rootBounds); - - switch (this) { - case MIDDLE: - default: - if (isLandscape(rootBounds)) { - mDividerBounds.left = rootBounds.width() / 2 - dividerOffset; - mDividerBounds.right = rootBounds.width() / 2 + dividerOffset; - mBounds1.left = rootBounds.width() / 2 + boundsOffset; - mBounds2.right = rootBounds.width() / 2 - boundsOffset; - } else { - mDividerBounds.top = rootBounds.height() / 2 - dividerOffset; - mDividerBounds.bottom = rootBounds.height() / 2 + dividerOffset; - mBounds1.bottom = rootBounds.height() / 2 - boundsOffset; - mBounds2.top = rootBounds.height() / 2 + boundsOffset; - } - } - } - - private boolean isLandscape(Rect bounds) { - return bounds.width() > bounds.height(); - } - - Rect mDividerBounds; - Rect mBounds1; - Rect mBounds2; - } - - /** - * WindowManger for app pair. Holds view hierarchy for the root task. - */ - private static final class AppPairWindowManager extends WindowlessWindowManager { - AppPairWindowManager(Configuration config, SurfaceControl rootSurface) { - super(config, rootSurface, null /* hostInputToken */); - } - - @Override - public void setTouchRegion(IBinder window, Region region) { - super.setTouchRegion(window, region); - } - - @Override - public SurfaceControl getSurfaceControl(IWindow window) { - return super.getSurfaceControl(window); - } - - @Override - public void setConfiguration(Configuration configuration) { - super.setConfiguration(configuration); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java index ef3e3e0220e7..f5aa852c87ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java @@ -20,11 +20,14 @@ import android.app.ActivityManager; import androidx.annotation.NonNull; +import com.android.wm.shell.common.annotations.ExternalThread; + import java.io.PrintWriter; /** * Interface to engage app pairs feature. */ +@ExternalThread public interface AppPairs { /** Pairs indicated tasks. */ boolean pair(int task1, int task2); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java deleted file mode 100644 index 41b5e47e7fa9..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java +++ /dev/null @@ -1,56 +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.wm.shell.apppairs; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -/** - * Stack divider for app pair. - */ -public class DividerView extends FrameLayout { - public DividerView(@NonNull Context context) { - super(context); - } - - public DividerView(@NonNull Context context, - @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - void show() { - post(() -> setVisibility(View.VISIBLE)); - } - - void hide() { - post(() -> setVisibility(View.INVISIBLE)); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index aa7355b61eda..40b41e11c8aa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1213,7 +1213,7 @@ public class BubbleController implements Bubbles { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { if (mStackView != null) { - mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight)); + mStackView.onImeVisibilityChanged(imeVisible, imeHeight); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 92d15c5feaca..fa5ac449cd54 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -30,6 +30,8 @@ import android.view.View; import androidx.annotation.IntDef; import androidx.annotation.Nullable; +import com.android.wm.shell.common.annotations.ExternalThread; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -40,6 +42,7 @@ import java.util.function.IntConsumer; /** * Interface to engage bubbles feature. */ +@ExternalThread public interface Bubbles { @Retention(SOURCE) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java deleted file mode 100644 index 96b9f86673fc..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java +++ /dev/null @@ -1,61 +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.wm.shell.common; - -import static android.os.Process.THREAD_PRIORITY_DISPLAY; - -import android.annotation.NonNull; -import android.os.HandlerThread; -import android.util.Singleton; - -/** - * A singleton thread for Shell to run animations on. - */ -public class AnimationThread extends HandlerThread { - private ShellExecutor mExecutor; - - private AnimationThread() { - super("wmshell.anim", THREAD_PRIORITY_DISPLAY); - } - - /** Get the singleton instance of this thread */ - public static AnimationThread instance() { - return sAnimationThreadSingleton.get(); - } - - /** - * @return a shared {@link ShellExecutor} associated with this thread - * @hide - */ - @NonNull - public ShellExecutor getExecutor() { - if (mExecutor == null) { - mExecutor = new HandlerExecutor(getThreadHandler()); - } - return mExecutor; - } - - private static final Singleton<AnimationThread> sAnimationThreadSingleton = - new Singleton<AnimationThread>() { - @Override - protected AnimationThread create() { - final AnimationThread animThread = new AnimationThread(); - animThread.start(); - return animThread; - } - }; -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java index 3263f79888d6..cb4584c41184 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java @@ -23,6 +23,10 @@ import android.view.IDisplayWindowRotationController; import android.view.IWindowManager; import android.window.WindowContainerTransaction; +import androidx.annotation.BinderThread; + +import com.android.wm.shell.common.annotations.ShellMainThread; + import java.util.ArrayList; /** @@ -35,39 +39,18 @@ public class DisplayChangeController { private final Handler mHandler; private final IWindowManager mWmService; + private final IDisplayWindowRotationController mControllerImpl; private final ArrayList<OnDisplayChangingListener> mRotationListener = new ArrayList<>(); private final ArrayList<OnDisplayChangingListener> mTmpListeners = new ArrayList<>(); - private final IDisplayWindowRotationController mDisplayRotationController = - new IDisplayWindowRotationController.Stub() { - @Override - public void onRotateDisplay(int displayId, final int fromRotation, - final int toRotation, IDisplayWindowRotationCallback callback) { - mHandler.post(() -> { - WindowContainerTransaction t = new WindowContainerTransaction(); - synchronized (mRotationListener) { - mTmpListeners.clear(); - // Make a local copy in case the handlers add/remove themselves. - mTmpListeners.addAll(mRotationListener); - } - for (OnDisplayChangingListener c : mTmpListeners) { - c.onRotateDisplay(displayId, fromRotation, toRotation, t); - } - try { - callback.continueRotateDisplay(toRotation, t); - } catch (RemoteException e) { - } - }); - } - }; - public DisplayChangeController(Handler mainHandler, IWindowManager wmService) { mHandler = mainHandler; mWmService = wmService; + mControllerImpl = new DisplayWindowRotationControllerImpl(); try { - mWmService.setDisplayWindowRotationController(mDisplayRotationController); + mWmService.setDisplayWindowRotationController(mControllerImpl); } catch (RemoteException e) { throw new RuntimeException("Unable to register rotation controller"); } @@ -91,10 +74,41 @@ public class DisplayChangeController { } } + private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation, + IDisplayWindowRotationCallback callback) { + WindowContainerTransaction t = new WindowContainerTransaction(); + synchronized (mRotationListener) { + mTmpListeners.clear(); + // Make a local copy in case the handlers add/remove themselves. + mTmpListeners.addAll(mRotationListener); + } + for (OnDisplayChangingListener c : mTmpListeners) { + c.onRotateDisplay(displayId, fromRotation, toRotation, t); + } + try { + callback.continueRotateDisplay(toRotation, t); + } catch (RemoteException e) { + } + } + + @BinderThread + private class DisplayWindowRotationControllerImpl + extends IDisplayWindowRotationController.Stub { + @Override + public void onRotateDisplay(int displayId, final int fromRotation, + final int toRotation, IDisplayWindowRotationCallback callback) { + mHandler.post(() -> { + DisplayChangeController.this.onRotateDisplay(displayId, fromRotation, toRotation, + callback); + }); + } + } + /** * Give a listener a chance to queue up configuration changes to execute as part of a * display rotation. The contents of {@link #onRotateDisplay} must run synchronously. */ + @ShellMainThread public interface OnDisplayChangingListener { /** * Called before the display is rotated. Contents of this method must run synchronously. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java index 418973204add..a413c052cb6c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java @@ -28,7 +28,10 @@ import android.view.Display; import android.view.IDisplayWindowListener; import android.view.IWindowManager; +import androidx.annotation.BinderThread; + import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener; +import com.android.wm.shell.common.annotations.ShellMainThread; import java.util.ArrayList; @@ -45,6 +48,7 @@ public class DisplayController { private final Context mContext; private final IWindowManager mWmService; private final DisplayChangeController mChangeController; + private final IDisplayWindowListener mDisplayContainerListener; private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>(); private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>(); @@ -57,119 +61,13 @@ public class DisplayController { return displayManager.getDisplay(displayId); } - private final IDisplayWindowListener mDisplayContainerListener = - new IDisplayWindowListener.Stub() { - @Override - public void onDisplayAdded(int displayId) { - mHandler.post(() -> { - synchronized (mDisplays) { - if (mDisplays.get(displayId) != null) { - return; - } - Display display = getDisplay(displayId); - if (display == null) { - // It's likely that the display is private to some app and thus not - // accessible by system-ui. - return; - } - DisplayRecord record = new DisplayRecord(); - record.mDisplayId = displayId; - record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext - : mContext.createDisplayContext(display); - record.mDisplayLayout = new DisplayLayout(record.mContext, display); - mDisplays.put(displayId, record); - for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { - mDisplayChangedListeners.get(i).onDisplayAdded(displayId); - } - } - }); - } - - @Override - public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { - mHandler.post(() -> { - synchronized (mDisplays) { - DisplayRecord dr = mDisplays.get(displayId); - if (dr == null) { - Slog.w(TAG, "Skipping Display Configuration change on non-added" - + " display."); - return; - } - Display display = getDisplay(displayId); - if (display == null) { - Slog.w(TAG, "Skipping Display Configuration change on invalid" - + " display. It may have been removed."); - return; - } - Context perDisplayContext = mContext; - if (displayId != Display.DEFAULT_DISPLAY) { - perDisplayContext = mContext.createDisplayContext(display); - } - dr.mContext = perDisplayContext.createConfigurationContext(newConfig); - dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); - for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { - mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( - displayId, newConfig); - } - } - }); - } - - @Override - public void onDisplayRemoved(int displayId) { - mHandler.post(() -> { - synchronized (mDisplays) { - if (mDisplays.get(displayId) == null) { - return; - } - for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { - mDisplayChangedListeners.get(i).onDisplayRemoved(displayId); - } - mDisplays.remove(displayId); - } - }); - } - - @Override - public void onFixedRotationStarted(int displayId, int newRotation) { - mHandler.post(() -> { - synchronized (mDisplays) { - if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { - Slog.w(TAG, "Skipping onFixedRotationStarted on unknown" - + " display, displayId=" + displayId); - return; - } - for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { - mDisplayChangedListeners.get(i).onFixedRotationStarted( - displayId, newRotation); - } - } - }); - } - - @Override - public void onFixedRotationFinished(int displayId) { - mHandler.post(() -> { - synchronized (mDisplays) { - if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { - Slog.w(TAG, "Skipping onFixedRotationFinished on unknown" - + " display, displayId=" + displayId); - return; - } - for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { - mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId); - } - } - }); - } - }; - public DisplayController(Context context, Handler handler, IWindowManager wmService) { mHandler = handler; mContext = context; mWmService = wmService; mChangeController = new DisplayChangeController(mHandler, mWmService); + mDisplayContainerListener = new DisplayWindowListenerImpl(); try { mWmService.registerDisplayWindowListener(mDisplayContainerListener); } catch (RemoteException e) { @@ -232,18 +130,146 @@ public class DisplayController { mChangeController.removeRotationListener(controller); } + private void onDisplayAdded(int displayId) { + synchronized (mDisplays) { + if (mDisplays.get(displayId) != null) { + return; + } + Display display = getDisplay(displayId); + if (display == null) { + // It's likely that the display is private to some app and thus not + // accessible by system-ui. + return; + } + DisplayRecord record = new DisplayRecord(); + record.mDisplayId = displayId; + record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext + : mContext.createDisplayContext(display); + record.mDisplayLayout = new DisplayLayout(record.mContext, display); + mDisplays.put(displayId, record); + for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { + mDisplayChangedListeners.get(i).onDisplayAdded(displayId); + } + } + } + + private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + synchronized (mDisplays) { + DisplayRecord dr = mDisplays.get(displayId); + if (dr == null) { + Slog.w(TAG, "Skipping Display Configuration change on non-added" + + " display."); + return; + } + Display display = getDisplay(displayId); + if (display == null) { + Slog.w(TAG, "Skipping Display Configuration change on invalid" + + " display. It may have been removed."); + return; + } + Context perDisplayContext = mContext; + if (displayId != Display.DEFAULT_DISPLAY) { + perDisplayContext = mContext.createDisplayContext(display); + } + dr.mContext = perDisplayContext.createConfigurationContext(newConfig); + dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); + for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { + mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( + displayId, newConfig); + } + } + } + + private void onDisplayRemoved(int displayId) { + synchronized (mDisplays) { + if (mDisplays.get(displayId) == null) { + return; + } + for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { + mDisplayChangedListeners.get(i).onDisplayRemoved(displayId); + } + mDisplays.remove(displayId); + } + } + + private void onFixedRotationStarted(int displayId, int newRotation) { + synchronized (mDisplays) { + if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { + Slog.w(TAG, "Skipping onFixedRotationStarted on unknown" + + " display, displayId=" + displayId); + return; + } + for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { + mDisplayChangedListeners.get(i).onFixedRotationStarted( + displayId, newRotation); + } + } + } + + private void onFixedRotationFinished(int displayId) { + synchronized (mDisplays) { + if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { + Slog.w(TAG, "Skipping onFixedRotationFinished on unknown" + + " display, displayId=" + displayId); + return; + } + for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { + mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId); + } + } + } + private static class DisplayRecord { int mDisplayId; Context mContext; DisplayLayout mDisplayLayout; } + @BinderThread + private class DisplayWindowListenerImpl extends IDisplayWindowListener.Stub { + @Override + public void onDisplayAdded(int displayId) { + mHandler.post(() -> { + DisplayController.this.onDisplayAdded(displayId); + }); + } + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + mHandler.post(() -> { + DisplayController.this.onDisplayConfigurationChanged(displayId, newConfig); + }); + } + + @Override + public void onDisplayRemoved(int displayId) { + mHandler.post(() -> { + DisplayController.this.onDisplayRemoved(displayId); + }); + } + + @Override + public void onFixedRotationStarted(int displayId, int newRotation) { + mHandler.post(() -> { + DisplayController.this.onFixedRotationStarted(displayId, newRotation); + }); + } + + @Override + public void onFixedRotationFinished(int displayId) { + mHandler.post(() -> { + DisplayController.this.onFixedRotationFinished(displayId); + }); + } + } + /** * Gets notified when a display is added/removed to the WM hierarchy and when a display's * window-configuration changes. * * @see IDisplayWindowListener */ + @ShellMainThread public interface OnDisplaysChangedListener { /** * Called when a display has been added to the WM hierarchy. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index ea18a19c2ee5..3fbd7ed0ec5d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; -import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; @@ -40,6 +39,8 @@ import android.view.WindowInsets; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import androidx.annotation.BinderThread; + import com.android.internal.view.IInputMethodManager; import java.util.ArrayList; @@ -197,6 +198,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mRotation = initialRotation; } + @BinderThread @Override public void insetsChanged(InsetsState insetsState) { mExecutor.execute(() -> { @@ -204,6 +206,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return; } + mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME); + final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME); final Rect newFrame = newSource.getFrame(); final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame(); @@ -216,6 +220,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged }); } + @BinderThread @Override public void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls) { @@ -266,6 +271,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } + @BinderThread @Override public void showInsets(int types, boolean fromIme) { if ((types & WindowInsets.Type.ime()) == 0) { @@ -275,6 +281,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */)); } + @BinderThread @Override public void hideInsets(int types, boolean fromIme) { if ((types & WindowInsets.Type.ime()) == 0) { @@ -284,6 +291,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */)); } + @BinderThread @Override public void topFocusedWindowChanged(String packageName) { // no-op diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java index cd75840b8c71..fa0a75c2d364 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java @@ -28,6 +28,17 @@ public class HandlerExecutor implements ShellExecutor { } @Override + public void execute(@NonNull Runnable command) { + if (mHandler.getLooper().isCurrentThread()) { + command.run(); + return; + } + if (!mHandler.post(command)) { + throw new RuntimeException(mHandler + " is probably exiting"); + } + } + + @Override public void executeDelayed(@NonNull Runnable r, long delayMillis) { if (!mHandler.postDelayed(r, delayMillis)) { throw new RuntimeException(mHandler + " is probably exiting"); @@ -38,11 +49,4 @@ public class HandlerExecutor implements ShellExecutor { public void removeCallbacks(@NonNull Runnable r) { mHandler.removeCallbacks(r); } - - @Override - public void execute(@NonNull Runnable command) { - if (!mHandler.post(command)) { - throw new RuntimeException(mHandler + " is probably exiting"); - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java index aafe2407a1ea..22b831b7565e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java @@ -16,13 +16,40 @@ package com.android.wm.shell.common; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; /** * Super basic Executor interface that adds support for delayed execution and removing callbacks. * Intended to wrap Handler while better-supporting testing. */ public interface ShellExecutor extends Executor { + + /** + * Executes the given runnable. If the caller is running on the same looper as this executor, + * the runnable must be executed immediately. + */ + @Override + void execute(Runnable runnable); + + /** + * Executes the given runnable in a blocking call. If the caller is running on the same looper + * as this executor, the runnable must be executed immediately. + * + * @throws InterruptedException if runnable does not return in the time specified by + * {@param waitTimeout} + */ + default void executeBlocking(Runnable runnable, int waitTimeout, TimeUnit waitTimeUnit) + throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + execute(() -> { + runnable.run(); + latch.countDown(); + }); + latch.await(waitTimeout, waitTimeUnit); + } + /** * See {@link android.os.Handler#postDelayed(Runnable, long)}. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java index 9cb125087cd9..7321dc88770d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java @@ -24,6 +24,10 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import android.window.WindowOrganizer; +import androidx.annotation.BinderThread; + +import com.android.wm.shell.common.annotations.ShellMainThread; + import java.util.ArrayList; /** @@ -151,6 +155,7 @@ public final class SyncTransactionQueue { mHandler.postDelayed(mOnReplyTimeout, REPLY_TIMEOUT); } + @BinderThread @Override public void onTransactionReady(int id, @NonNull SurfaceControl.Transaction t) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java index 0f6dd93f9c16..5e077188c415 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java @@ -19,12 +19,10 @@ package com.android.wm.shell.common; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ITaskStackListener; -import android.app.TaskInfo; import android.content.ComponentName; import android.os.IBinder; import androidx.annotation.BinderThread; -import androidx.annotation.MainThread; /** * An interface to track task stack changes. Classes should implement this instead of @@ -32,85 +30,61 @@ import androidx.annotation.MainThread; */ public interface TaskStackListenerCallback { - @MainThread default void onRecentTaskListUpdated() { } - @MainThread default void onRecentTaskListFrozenChanged(boolean frozen) { } @BinderThread default void onTaskStackChangedBackground() { } - @MainThread default void onTaskStackChanged() { } - @MainThread default void onTaskProfileLocked(int taskId, int userId) { } - @MainThread default void onTaskDisplayChanged(int taskId, int newDisplayId) { } - @MainThread default void onTaskCreated(int taskId, ComponentName componentName) { } - @MainThread default void onTaskRemoved(int taskId) { } - @MainThread default void onTaskMovedToFront(int taskId) { } - @MainThread default void onTaskMovedToFront(RunningTaskInfo taskInfo) { onTaskMovedToFront(taskInfo.taskId); } - @MainThread default void onTaskDescriptionChanged(RunningTaskInfo taskInfo) { } - @MainThread default void onTaskSnapshotChanged(int taskId, ActivityManager.TaskSnapshot snapshot) { } - @MainThread default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { } - @MainThread default void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { } - @MainThread default void onActivityPinned(String packageName, int userId, int taskId, int stackId) { } - @MainThread default void onActivityUnpinned() { } - @MainThread default void onActivityForcedResizable(String packageName, int taskId, int reason) { } - @MainThread default void onActivityDismissingDockedStack() { } - @MainThread default void onActivityLaunchOnSecondaryDisplayFailed() { } - @MainThread default void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo) { onActivityLaunchOnSecondaryDisplayFailed(); } - @MainThread default void onActivityLaunchOnSecondaryDisplayRerouted() { } - @MainThread default void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo) { onActivityLaunchOnSecondaryDisplayRerouted(); } - @MainThread default void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { } - @MainThread default void onActivityRotation(int displayId) { } - @MainThread default void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java new file mode 100644 index 000000000000..4009ad21b9b8 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java @@ -0,0 +1,18 @@ +package com.android.wm.shell.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Qualifier; + +/** + * Annotates a method that or qualifies a provider runs aligned to the Choreographer SF vsync + * instead of the app vsync. + */ +@Documented +@Inherited +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface ChoreographerSfVsync {}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java new file mode 100644 index 000000000000..7560f71d1f98 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java @@ -0,0 +1,15 @@ +package com.android.wm.shell.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Qualifier; + +/** Annotates a method or class that is called from an external thread to the Shell threads. */ +@Documented +@Inherited +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface ExternalThread {}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java new file mode 100644 index 000000000000..0479f8780c79 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java @@ -0,0 +1,15 @@ +package com.android.wm.shell.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Qualifier; + +/** Annotates a method or qualifies a provider that runs on the Shell animation-thread */ +@Documented +@Inherited +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface ShellAnimationThread {}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java new file mode 100644 index 000000000000..423f4ce3bfd4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java @@ -0,0 +1,15 @@ +package com.android.wm.shell.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Qualifier; + +/** Annotates a method or qualifies a provider that runs on the Shell main-thread */ +@Documented +@Inherited +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface ShellMainThread {}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java new file mode 100644 index 000000000000..50d9fe8629ac --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -0,0 +1,171 @@ +/* + * 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.wm.shell.common.split; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.SurfaceControlViewHost; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.WindowManager; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.policy.DividerSnapAlgorithm; + +/** + * Stack divider for app pair. + */ +// TODO(b/172704238): add handle view to indicate touching status. +public class DividerView extends FrameLayout implements View.OnTouchListener { + private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + + private SplitLayout mSplitLayout; + private SurfaceControlViewHost mViewHost; + private DragListener mDragListener; + + private VelocityTracker mVelocityTracker; + private boolean mMoving; + private int mStartPos; + + public DividerView(@NonNull Context context) { + super(context); + } + + public DividerView(@NonNull Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + /** Sets up essential dependencies of the divider bar. */ + public void setup( + SplitLayout layout, + SurfaceControlViewHost viewHost, + @Nullable DragListener dragListener) { + mSplitLayout = layout; + mViewHost = viewHost; + mDragListener = dragListener; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + setOnTouchListener(this); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mSplitLayout == null) { + return false; + } + + final int action = event.getAction() & MotionEvent.ACTION_MASK; + final boolean isLandscape = isLandscape(); + // Using raw xy to prevent lost track of motion events while moving divider bar. + final int touchPos = isLandscape ? (int) event.getRawX() : (int) event.getRawY(); + switch (action) { + case MotionEvent.ACTION_DOWN: + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(event); + setSlippery(false); + mStartPos = touchPos; + mMoving = false; + break; + case MotionEvent.ACTION_MOVE: + mVelocityTracker.addMovement(event); + if (!mMoving && Math.abs(touchPos - mStartPos) > mTouchSlop) { + mStartPos = touchPos; + mMoving = true; + if (mDragListener != null) { + mDragListener.onDragStart(); + } + } + if (mMoving) { + final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos; + mSplitLayout.updateDividePosition(position); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mVelocityTracker.addMovement(event); + mVelocityTracker.computeCurrentVelocity(1000 /* units */); + final float velocity = isLandscape + ? mVelocityTracker.getXVelocity() + : mVelocityTracker.getYVelocity(); + setSlippery(true); + mMoving = false; + if (mDragListener != null) { + mDragListener.onDragEnd(); + } + + final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos; + final DividerSnapAlgorithm.SnapTarget snapTarget = + mSplitLayout.findSnapTarget(position, velocity); + mSplitLayout.setSnapTarget(snapTarget); + break; + } + return true; + } + + private void setSlippery(boolean slippery) { + if (mViewHost == null) { + return; + } + + final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); + final boolean isSlippery = (lp.flags & FLAG_SLIPPERY) != 0; + if (isSlippery == slippery) { + return; + } + + if (slippery) { + lp.flags |= FLAG_SLIPPERY; + } else { + lp.flags &= ~FLAG_SLIPPERY; + } + mViewHost.relayout(lp); + } + + private boolean isLandscape() { + return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE; + } + + /** Monitors dragging action of the divider bar. */ + // TODO(b/172704238): add listeners to deal with resizing state of the app windows. + public interface DragListener { + /** Called when start dragging. */ + void onDragStart(); + /** Called when stop dragging. */ + void onDragEnd(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java new file mode 100644 index 000000000000..e11037f55cfa --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.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 com.android.wm.shell.common.split; + +import static android.view.WindowManager.DOCKED_LEFT; +import static android.view.WindowManager.DOCKED_TOP; + +import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END; +import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Rect; +import android.view.SurfaceControl; + +import androidx.annotation.Nullable; + +import com.android.internal.policy.DividerSnapAlgorithm; + +/** + * Records and handles layout of splits. Helps to calculate proper bounds when configuration or + * divide position changes. + */ +public class SplitLayout { + private final int mDividerWindowWidth; + private final int mDividerInsets; + private final int mDividerSize; + + private final Rect mRootBounds = new Rect(); + private final Rect mDividerBounds = new Rect(); + private final Rect mBounds1 = new Rect(); + private final Rect mBounds2 = new Rect(); + private final LayoutChangeListener mLayoutChangeListener; + private final SplitWindowManager mSplitWindowManager; + + private Context mContext; + private DividerSnapAlgorithm mDividerSnapAlgorithm; + private int mDividePosition; + + public SplitLayout(Context context, Configuration configuration, + LayoutChangeListener layoutChangeListener, SurfaceControl rootLeash) { + mContext = context.createConfigurationContext(configuration); + mLayoutChangeListener = layoutChangeListener; + mSplitWindowManager = new SplitWindowManager(mContext, configuration, rootLeash); + + mDividerWindowWidth = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.docked_stack_divider_thickness); + mDividerInsets = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.docked_stack_divider_insets); + mDividerSize = mDividerWindowWidth - mDividerInsets * 2; + + mRootBounds.set(configuration.windowConfiguration.getBounds()); + mDividerSnapAlgorithm = getSnapAlgorithm(context.getResources(), mRootBounds); + mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position; + updateBounds(mDividePosition); + } + + /** Gets bounds of the primary split. */ + public Rect getBounds1() { + return mBounds1; + } + + /** Gets bounds of the secondary split. */ + public Rect getBounds2() { + return mBounds2; + } + + /** Gets bounds of divider window. */ + public Rect getDividerBounds() { + return mDividerBounds; + } + + /** Returns leash of the current divider bar. */ + @Nullable + public SurfaceControl getDividerLeash() { + return mSplitWindowManager == null ? null : mSplitWindowManager.getSurfaceControl(); + } + + int getDividePosition() { + return mDividePosition; + } + + /** Applies new configuration, returns {@code false} if there's no effect to the layout. */ + public boolean updateConfiguration(Configuration configuration) { + final Rect rootBounds = configuration.windowConfiguration.getBounds(); + if (mRootBounds.equals(rootBounds)) { + return false; + } + + mContext = mContext.createConfigurationContext(configuration); + mSplitWindowManager.setConfiguration(configuration); + mRootBounds.set(rootBounds); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext.getResources(), mRootBounds); + mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position; + updateBounds(mDividePosition); + release(); + init(); + return true; + } + + /** Updates recording bounds of divider window and both of the splits. */ + private void updateBounds(int position) { + mDividerBounds.set(mRootBounds); + mBounds1.set(mRootBounds); + mBounds2.set(mRootBounds); + if (isLandscape(mRootBounds)) { + mDividerBounds.left = position - mDividerInsets; + mDividerBounds.right = mDividerBounds.left + mDividerWindowWidth; + mBounds1.right = mBounds1.left + position; + mBounds2.left = mBounds1.right + mDividerSize; + } else { + mDividerBounds.top = position - mDividerInsets; + mDividerBounds.bottom = mDividerBounds.top + mDividerWindowWidth; + mBounds1.bottom = mBounds1.top + position; + mBounds2.top = mBounds1.bottom + mDividerSize; + } + } + + /** Inflates {@link DividerView} on the root surface. */ + public void init() { + mSplitWindowManager.init(this); + } + + /** Releases the surface holding the current {@link DividerView}. */ + public void release() { + mSplitWindowManager.release(); + } + + /** + * Updates bounds with the passing position. Usually used to update recording bounds while + * performing animation or dragging divider bar to resize the splits. + */ + public void updateDividePosition(int position) { + updateBounds(position); + mLayoutChangeListener.onBoundsChanging(this); + } + + /** + * Sets new divide position and updates bounds correspondingly. Notifies listener if the new + * target indicates dismissing split. + */ + public void setSnapTarget(DividerSnapAlgorithm.SnapTarget snapTarget) { + switch(snapTarget.flag) { + case FLAG_DISMISS_START: + mLayoutChangeListener.onSnappedToDismiss(false /* snappedToEnd */); + break; + case FLAG_DISMISS_END: + mLayoutChangeListener.onSnappedToDismiss(true /* snappedToEnd */); + break; + default: + mDividePosition = snapTarget.position; + updateBounds(mDividePosition); + mLayoutChangeListener.onBoundsChanged(this); + break; + } + } + + /** + * Returns {@link DividerSnapAlgorithm.SnapTarget} which matches passing position and velocity. + */ + public DividerSnapAlgorithm.SnapTarget findSnapTarget(int position, float velocity) { + return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity); + } + + private DividerSnapAlgorithm getSnapAlgorithm(Resources resources, Rect rootBounds) { + final boolean isLandscape = isLandscape(rootBounds); + return new DividerSnapAlgorithm( + resources, + rootBounds.width(), + rootBounds.height(), + mDividerSize, + !isLandscape, + new Rect() /* insets */, + isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */); + } + + private static boolean isLandscape(Rect bounds) { + return bounds.width() > bounds.height(); + } + + /** Listens layout change event. */ + public interface LayoutChangeListener { + /** Calls when dismissing split. */ + void onSnappedToDismiss(boolean snappedToEnd); + /** Calls when the bounds is changing due to animation or dragging divider bar. */ + void onBoundsChanging(SplitLayout layout); + /** Calls when the target bounds changed. */ + void onBoundsChanged(SplitLayout layout); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java new file mode 100644 index 000000000000..542867d83e29 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -0,0 +1,115 @@ +/* + * 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.wm.shell.common.split; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.Binder; +import android.os.IBinder; +import android.view.IWindow; +import android.view.LayoutInflater; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.WindowManager; +import android.view.WindowlessWindowManager; + +import androidx.annotation.Nullable; + +import com.android.wm.shell.R; + +/** + * Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split. + */ +public final class SplitWindowManager extends WindowlessWindowManager { + private static final String DIVIDER_WINDOW_TITLE = "SplitDivider"; + + private Context mContext; + private SurfaceControlViewHost mViewHost; + + public SplitWindowManager(Context context, Configuration config, SurfaceControl rootSurface) { + super(config, rootSurface, null /* hostInputToken */); + mContext = context.createConfigurationContext(config); + } + + @Override + public void setTouchRegion(IBinder window, Region region) { + super.setTouchRegion(window, region); + } + + @Override + public SurfaceControl getSurfaceControl(IWindow window) { + return super.getSurfaceControl(window); + } + + @Override + public void setConfiguration(Configuration configuration) { + super.setConfiguration(configuration); + mContext = mContext.createConfigurationContext(configuration); + } + + /** Inflates {@link DividerView} on to the root surface. */ + void init(SplitLayout splitLayout) { + if (mViewHost == null) { + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + } + + final Rect dividerBounds = splitLayout.getDividerBounds(); + final DividerView dividerView = (DividerView) LayoutInflater.from(mContext) + .inflate(R.layout.split_divider, null /* root */); + + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + dividerBounds.width(), dividerBounds.height(), TYPE_DOCK_DIVIDER, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH + | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY, + PixelFormat.TRANSLUCENT); + lp.token = new Binder(); + lp.setTitle(DIVIDER_WINDOW_TITLE); + lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; + mViewHost.setView(dividerView, lp); + dividerView.setup(splitLayout, mViewHost, null /* dragListener */); + } + + /** + * Releases the surface control of the current {@link DividerView} and tear down the view + * hierarchy. + */ + void release() { + if (mViewHost == null) return; + mViewHost.release(); + mViewHost = null; + } + + /** + * Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not + * feasible. + */ + @Nullable + SurfaceControl getSurfaceControl() { + return mViewHost == null ? null : getSurfaceControl(mViewHost.getWindowToken()); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java index 38e0519b7a90..3a2f0da6bf03 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java @@ -20,11 +20,14 @@ import android.content.res.Configuration; import androidx.annotation.NonNull; +import com.android.wm.shell.common.annotations.ExternalThread; + import java.io.PrintWriter; /** * Interface to engage hide display cutout feature. */ +@ExternalThread public interface HideDisplayCutout { /** * Notifies {@link Configuration} changed. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java index 9bb709f9a82a..821a00703adf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java @@ -18,6 +18,7 @@ package com.android.wm.shell.onehanded; import androidx.annotation.NonNull; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; import java.io.PrintWriter; @@ -25,6 +26,7 @@ import java.io.PrintWriter; /** * Interface to engage one handed feature. */ +@ExternalThread public interface OneHanded { /** * Return one handed settings enabled or not. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java index 993e0e7ed016..5593268588fd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java @@ -19,12 +19,16 @@ package com.android.wm.shell.pip; import android.app.RemoteAction; import android.content.ComponentName; import android.content.pm.ParceledListSlice; +import android.os.RemoteException; import android.view.DisplayInfo; -import android.view.IPinnedStackController; import android.view.IPinnedStackListener; +import android.view.WindowManagerGlobal; + +import androidx.annotation.BinderThread; + +import com.android.wm.shell.common.ShellExecutor; import java.util.ArrayList; -import java.util.List; /** * PinnedStackListener that simply forwards all calls to each listener added via @@ -32,8 +36,15 @@ import java.util.List; * {@link com.android.server.wm.WindowManagerService#registerPinnedStackListener} replaces any * previously set listener. */ -public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { - private List<PinnedStackListener> mListeners = new ArrayList<>(); +public class PinnedStackListenerForwarder { + + private final IPinnedStackListener mListenerImpl = new PinnedStackListenerImpl(); + private final ShellExecutor mShellMainExecutor; + private final ArrayList<PinnedStackListener> mListeners = new ArrayList<>(); + + public PinnedStackListenerForwarder(ShellExecutor shellMainExecutor) { + mShellMainExecutor = shellMainExecutor; + } /** Adds a listener to receive updates from the WindowManagerService. */ public void addListener(PinnedStackListener listener) { @@ -45,69 +56,110 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { mListeners.remove(listener); } - @Override - public void onListenerRegistered(IPinnedStackController controller) { - for (PinnedStackListener listener : mListeners) { - listener.onListenerRegistered(controller); - } + public void register(int displayId) throws RemoteException { + WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener( + displayId, mListenerImpl); } - @Override - public void onMovementBoundsChanged(boolean fromImeAdjustment) { + private void onMovementBoundsChanged(boolean fromImeAdjustment) { for (PinnedStackListener listener : mListeners) { listener.onMovementBoundsChanged(fromImeAdjustment); } } - @Override - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { + private void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { for (PinnedStackListener listener : mListeners) { listener.onImeVisibilityChanged(imeVisible, imeHeight); } } - @Override - public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { + private void onActionsChanged(ParceledListSlice<RemoteAction> actions) { for (PinnedStackListener listener : mListeners) { listener.onActionsChanged(actions); } } - @Override - public void onActivityHidden(ComponentName componentName) { + private void onActivityHidden(ComponentName componentName) { for (PinnedStackListener listener : mListeners) { listener.onActivityHidden(componentName); } } - @Override - public void onDisplayInfoChanged(DisplayInfo displayInfo) { + private void onDisplayInfoChanged(DisplayInfo displayInfo) { for (PinnedStackListener listener : mListeners) { listener.onDisplayInfoChanged(displayInfo); } } - @Override - public void onConfigurationChanged() { + private void onConfigurationChanged() { for (PinnedStackListener listener : mListeners) { listener.onConfigurationChanged(); } } - @Override - public void onAspectRatioChanged(float aspectRatio) { + private void onAspectRatioChanged(float aspectRatio) { for (PinnedStackListener listener : mListeners) { listener.onAspectRatioChanged(aspectRatio); } } + @BinderThread + private class PinnedStackListenerImpl extends IPinnedStackListener.Stub { + @Override + public void onMovementBoundsChanged(boolean fromImeAdjustment) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onMovementBoundsChanged(fromImeAdjustment); + }); + } + + @Override + public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onImeVisibilityChanged(imeVisible, imeHeight); + }); + } + + @Override + public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onActionsChanged(actions); + }); + } + + @Override + public void onActivityHidden(ComponentName componentName) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onActivityHidden(componentName); + }); + } + + @Override + public void onDisplayInfoChanged(DisplayInfo displayInfo) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onDisplayInfoChanged(displayInfo); + }); + } + + @Override + public void onConfigurationChanged() { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onConfigurationChanged(); + }); + } + + @Override + public void onAspectRatioChanged(float aspectRatio) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onAspectRatioChanged(aspectRatio); + }); + } + } + /** * A counterpart of {@link IPinnedStackListener} with empty implementations. * Subclasses can ignore those methods they do not intend to take action upon. */ public static class PinnedStackListener { - public void onListenerRegistered(IPinnedStackController controller) {} - public void onMovementBoundsChanged(boolean fromImeAdjustment) {} public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index 9fa222ad4fdd..da9ce0aacedc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -17,12 +17,12 @@ package com.android.wm.shell.pip; import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.PictureInPictureParams; import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.graphics.Rect; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.pip.phone.PipTouchHandler; import java.io.PrintWriter; @@ -31,6 +31,7 @@ import java.util.function.Consumer; /** * Interface to engage picture in picture feature. */ +@ExternalThread public interface Pip { /** * Closes PIP (PIPed activity and PIP system UI). diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index d82946269ee8..fe018115408a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -54,6 +54,7 @@ public class PipAnimationController { public static final int TRANSITION_DIRECTION_LEAVE_PIP = 3; public static final int TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN = 4; public static final int TRANSITION_DIRECTION_REMOVE_STACK = 5; + public static final int TRANSITION_DIRECTION_SNAP_AFTER_RESIZE = 6; @IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = { TRANSITION_DIRECTION_NONE, @@ -61,7 +62,8 @@ public class PipAnimationController { TRANSITION_DIRECTION_TO_PIP, TRANSITION_DIRECTION_LEAVE_PIP, TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN, - TRANSITION_DIRECTION_REMOVE_STACK + TRANSITION_DIRECTION_REMOVE_STACK, + TRANSITION_DIRECTION_SNAP_AFTER_RESIZE }) @Retention(RetentionPolicy.SOURCE) public @interface TransitionDirection {} @@ -109,13 +111,27 @@ public class PipAnimationController { } @SuppressWarnings("unchecked") + /** + * Construct and return an animator that animates from the {@param startBounds} to the + * {@param endBounds} with the given {@param direction}. If {@param direction} is type + * {@link ANIM_TYPE_BOUNDS}, then {@param sourceHintRect} will be used to animate + * in a better, more smooth manner. + * + * In the case where one wants to start animation during an intermediate animation (for example, + * if the user is currently doing a pinch-resize, and upon letting go now PiP needs to animate + * to the correct snap fraction region), then provide the base bounds, which is current PiP + * leash bounds before transformation/any animation. This is so when we try to construct + * the different transformation matrices for the animation, we are constructing this based off + * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed. + */ @VisibleForTesting - public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds, - Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction) { + public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect baseBounds, + Rect startBounds, Rect endBounds, Rect sourceHintRect, + @PipAnimationController.TransitionDirection int direction) { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( - PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect, - direction)); + PipTransitionAnimator.ofBounds(leash, startBounds, startBounds, endBounds, + sourceHintRect, direction)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA && mCurrentAnimator.isRunning()) { // If we are still animating the fade into pip, then just move the surface and ensure @@ -130,8 +146,8 @@ public class PipAnimationController { } else { mCurrentAnimator.cancel(); mCurrentAnimator = setupPipTransitionAnimator( - PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect, - direction)); + PipTransitionAnimator.ofBounds(leash, baseBounds, startBounds, endBounds, + sourceHintRect, direction)); } return mCurrentAnimator; } @@ -180,6 +196,7 @@ public class PipAnimationController { private final @AnimationType int mAnimationType; private final Rect mDestinationBounds = new Rect(); + private T mBaseValue; protected T mCurrentValue; protected T mStartValue; private T mEndValue; @@ -190,10 +207,11 @@ public class PipAnimationController { private @TransitionDirection int mTransitionDirection; private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType, - Rect destinationBounds, T startValue, T endValue) { + Rect destinationBounds, T baseValue, T startValue, T endValue) { mLeash = leash; mAnimationType = animationType; mDestinationBounds.set(destinationBounds); + mBaseValue = baseValue; mStartValue = startValue; mEndValue = endValue; addListener(this); @@ -263,6 +281,10 @@ public class PipAnimationController { return mStartValue; } + T getBaseValue() { + return mBaseValue; + } + @VisibleForTesting public T getEndValue() { return mEndValue; @@ -334,7 +356,7 @@ public class PipAnimationController { static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash, Rect destinationBounds, float startValue, float endValue) { return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA, - destinationBounds, startValue, endValue) { + destinationBounds, startValue, startValue, endValue) { @Override void applySurfaceControlTransaction(SurfaceControl leash, SurfaceControl.Transaction tx, float fraction) { @@ -367,7 +389,7 @@ public class PipAnimationController { } static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash, - Rect startValue, Rect endValue, Rect sourceHintRect, + Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction) { // Just for simplicity we'll interpolate between the source rect hint insets and empty // insets to calculate the window crop @@ -375,7 +397,7 @@ public class PipAnimationController { if (isOutPipDirection(direction)) { initialSourceValue = new Rect(endValue); } else { - initialSourceValue = new Rect(startValue); + initialSourceValue = new Rect(baseValue); } final Rect sourceHintRectInsets; @@ -391,22 +413,24 @@ public class PipAnimationController { // construct new Rect instances in case they are recycled return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS, - endValue, new Rect(startValue), new Rect(endValue)) { + endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) { private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect()); private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect()); @Override void applySurfaceControlTransaction(SurfaceControl leash, SurfaceControl.Transaction tx, float fraction) { + final Rect base = getBaseValue(); final Rect start = getStartValue(); final Rect end = getEndValue(); Rect bounds = mRectEvaluator.evaluate(fraction, start, end); setCurrentValue(bounds); if (inScaleTransition() || sourceHintRect == null) { + if (isOutPipDirection(direction)) { getSurfaceTransactionHelper().scale(tx, leash, end, bounds); } else { - getSurfaceTransactionHelper().scale(tx, leash, start, bounds); + getSurfaceTransactionHelper().scale(tx, leash, base, bounds); } } else { final Rect insets; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java new file mode 100644 index 000000000000..8d9ad4d1b96c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import android.annotation.Nullable; +import android.app.ActivityManager.RunningTaskInfo; +import android.app.RemoteAction; +import android.content.pm.ParceledListSlice; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.view.SurfaceControl; +import android.view.WindowManager; + +/** + * Interface to allow {@link com.android.wm.shell.pip.PipTaskOrganizer} to call into + * PiP menu when certain events happen (task appear/vanish, PiP move, etc.) + */ +public interface PipMenuController { + + String MENU_WINDOW_TITLE = "PipMenuView"; + + /** + * Called when + * {@link PipTaskOrganizer#onTaskAppeared(RunningTaskInfo, SurfaceControl)} + * is called. + */ + void attach(SurfaceControl leash); + + /** + * Called when + * {@link PipTaskOrganizer#onTaskVanished(RunningTaskInfo)} is called. + */ + void detach(); + + /** + * Check if menu is visible or not. + */ + boolean isMenuVisible(); + + /** + * Show the PIP menu. + */ + void showMenu(); + + /** + * Given a set of actions, update the menu. + */ + void setAppActions(ParceledListSlice<RemoteAction> appActions); + + /** + * Resize the PiP menu with the given bounds. The PiP SurfaceControl is given if there is a + * need to synchronize the movements on the same frame as PiP. + */ + default void resizePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, + Rect destinationBounds) {} + + /** + * Move the PiP menu with the given bounds. The PiP SurfaceControl is given if there is a + * need to synchronize the movements on the same frame as PiP. + */ + default void movePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, + Rect destinationBounds) {} + + /** + * Update the PiP menu with the given bounds for re-layout purposes. + */ + default void updateMenuBounds(Rect destinationBounds) {} + + /** + * Returns a default LayoutParams for the PIP Menu. + * @param width the PIP stack width. + * @param height the PIP stack height. + */ + default WindowManager.LayoutParams getPipMenuLayoutParams(String title, int width, int height) { + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, + TYPE_APPLICATION_OVERLAY, + FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY | FLAG_NOT_TOUCHABLE, + PixelFormat.TRANSLUCENT); + lp.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY; + lp.setTitle(title); + return lp; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 7cc2a419354e..167b9f9975f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -30,6 +30,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_NONE; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME; +import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SNAP_AFTER_RESIZE; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; @@ -63,7 +64,6 @@ import com.android.internal.os.SomeArgs; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.pip.phone.PipMenuActivityController; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipUpdateThread; import com.android.wm.shell.splitscreen.SplitScreen; @@ -135,8 +135,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final Handler mUpdateHandler; private final PipBoundsState mPipBoundsState; private final PipBoundsAlgorithm mPipBoundsAlgorithm; - // TODO(b/172286265): Remove dependency on .pip.PHONE.PipMenuActivityController - private final PipMenuActivityController mMenuActivityController; + private final @NonNull PipMenuController mPipMenuController; private final PipAnimationController mPipAnimationController; private final PipUiEventLogger mPipUiEventLoggerLogger; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); @@ -264,7 +263,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState, @NonNull PipBoundsAlgorithm boundsHandler, - PipMenuActivityController menuActivityController, + @NonNull PipMenuController pipMenuController, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, @NonNull DisplayController displayController, @@ -274,7 +273,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsState = pipBoundsState; mPipBoundsAlgorithm = boundsHandler; - mMenuActivityController = menuActivityController; + mPipMenuController = pipMenuController; mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = surfaceTransactionHelper; @@ -501,9 +500,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mOnDisplayIdChangeCallback.accept(info.displayId); } - if (mMenuActivityController != null) { - mMenuActivityController.onTaskAppeared(); - } + mPipMenuController.attach(leash); + if (mShouldIgnoreEnteringPipTransition) { final Rect destinationBounds = mPipBoundsState.getBounds(); @@ -674,9 +672,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPictureInPictureParams = null; mState = State.UNDEFINED; mPipUiEventLoggerLogger.setTaskInfo(null); - if (mMenuActivityController != null) { - mMenuActivityController.onTaskVanished(); - } + mPipMenuController.detach(); } @Override @@ -819,6 +815,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback); } + /** + * Animates resizing of the pinned stack given the duration and start bounds. + * This is used when the starting bounds is not the current PiP bounds. + */ + public void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration, + Consumer<Rect> updateBoundsCallback) { + if (mShouldDeferEnteringPip) { + Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred"); + return; + } + scheduleAnimateResizePip(fromBounds, toBounds, null /* sourceHintRect */, + TRANSITION_DIRECTION_SNAP_AFTER_RESIZE, duration, updateBoundsCallback); + } + private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, int durationMs, Consumer<Rect> updateBoundsCallback) { @@ -956,9 +966,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) .round(tx, mLeash, mState.isInPip()); - if (mMenuActivityController != null && mMenuActivityController.isMenuVisible()) { + if (mPipMenuController.isMenuVisible()) { runOnMainHandler(() -> - mMenuActivityController.resizePipMenu(mLeash, tx, destinationBounds)); + mPipMenuController.resizePipMenu(mLeash, tx, destinationBounds)); } else { tx.apply(); } @@ -982,9 +992,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds); - if (mMenuActivityController != null && mMenuActivityController.isMenuVisible()) { + if (mPipMenuController.isMenuVisible()) { runOnMainHandler(() -> - mMenuActivityController.movePipMenu(mLeash, tx, destinationBounds)); + mPipMenuController.movePipMenu(mLeash, tx, destinationBounds)); } else { tx.apply(); } @@ -1001,8 +1011,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (direction == TRANSITION_DIRECTION_REMOVE_STACK) { removePipImmediately(); return; - } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA - && mMenuActivityController != null) { + } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) { // TODO: Synchronize this correctly in #applyEnterPipSyncTransaction finishResizeForMenu(destinationBounds); return; @@ -1015,13 +1024,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } private void finishResizeForMenu(Rect destinationBounds) { - if (mMenuActivityController == null) { - if (DEBUG) Log.d(TAG, "mMenuActivityController is null"); - return; - } runOnMainHandler(() -> { - mMenuActivityController.movePipMenu(null, null, destinationBounds); - mMenuActivityController.updateMenuBounds(destinationBounds); + mPipMenuController.movePipMenu(null, null, destinationBounds); + mPipMenuController.updateMenuBounds(destinationBounds); }); } @@ -1083,8 +1088,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Log.w(TAG, "Abort animation, invalid leash"); return; } + Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE + ? mPipBoundsState.getBounds() : currentBounds; mPipAnimationController - .getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect, direction) + .getAnimator(mLeash, baseBounds, currentBounds, destinationBounds, sourceHintRect, + direction) .setTransitionDirection(direction) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(durationMs) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index d4217553ef2d..5db8f3d7ef40 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -18,12 +18,6 @@ package com.android.wm.shell.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; -import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; -import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; -import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; import android.annotation.Nullable; @@ -33,7 +27,6 @@ import android.app.RemoteAction; import android.content.Context; import android.content.pm.ParceledListSlice; import android.graphics.Matrix; -import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.os.Debug; @@ -44,27 +37,26 @@ import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; -import android.view.WindowManager; import android.view.WindowManagerGlobal; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipMediaController.ActionListener; +import com.android.wm.shell.pip.PipMenuController; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; /** - * Manages the PiP menu activity which can show menu options or a scrim. + * Manages the PiP menu view which can show menu options or a scrim. * * The current media session provides actions whenever there are no valid actions provided by the * current PiP activity. Otherwise, those actions always take precedence. */ -public class PipMenuActivityController { +public class PhonePipMenuController implements PipMenuController { private static final String TAG = "PipMenuActController"; - private static final String MENU_WINDOW_TITLE = "PipMenuView"; private static final boolean DEBUG = false; public static final int MENU_STATE_NONE = 0; @@ -124,7 +116,7 @@ public class PipMenuActivityController { } }; - public PipMenuActivityController(Context context, + public PhonePipMenuController(Context context, PipMediaController mediaController, SystemWindows systemWindows) { mContext = context; mMediaController = mediaController; @@ -138,20 +130,22 @@ public class PipMenuActivityController { /** * Attach the menu when the PiP task first appears. */ - public void onTaskAppeared() { + @Override + public void attach(SurfaceControl leash) { attachPipMenuView(); } /** * Detach the menu when the PiP task is gone. */ - public void onTaskVanished() { + @Override + public void detach() { hideMenu(); detachPipMenuView(); } - public void onPinnedStackAnimationEnded() { + void onPinnedStackAnimationEnded() { if (isMenuVisible()) { mPipMenuView.onPipAnimationEnded(); } @@ -163,7 +157,9 @@ public class PipMenuActivityController { detachPipMenuView(); } mPipMenuView = new PipMenuView(mContext, this); - mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(0, 0), 0, SHELL_ROOT_LAYER_PIP); + mSystemWindows.addView(mPipMenuView, + getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), + 0, SHELL_ROOT_LAYER_PIP); } private void detachPipMenuView() { @@ -181,9 +177,11 @@ public class PipMenuActivityController { * Updates the layout parameters of the menu. * @param destinationBounds New Menu bounds. */ + @Override public void updateMenuBounds(Rect destinationBounds) { mSystemWindows.updateViewLayout(mPipMenuView, - getPipMenuLayoutParams(destinationBounds.width(), destinationBounds.height())); + getPipMenuLayoutParams(MENU_WINDOW_TITLE, destinationBounds.width(), + destinationBounds.height())); } /** @@ -206,6 +204,16 @@ public class PipMenuActivityController { } /** + * When other components requests the menu controller directly to show the menu, we must + * first fire off the request to the other listeners who will then propagate the call + * back to the controller with the right parameters. + */ + @Override + public void showMenu() { + mListeners.forEach(Listener::onPipShowMenu); + } + + /** * Similar to {@link #showMenu(int, Rect, boolean, boolean, boolean)} but only show the menu * upon PiP window transition is finished. */ @@ -250,6 +258,7 @@ public class PipMenuActivityController { /** * Move the PiP menu, which does a translation and possibly a scale transformation. */ + @Override public void movePipMenu(@Nullable SurfaceControl pipLeash, @Nullable SurfaceControl.Transaction t, Rect destinationBounds) { @@ -290,6 +299,7 @@ public class PipMenuActivityController { /** * Does an immediate window crop of the PiP menu. */ + @Override public void resizePipMenu(@Nullable SurfaceControl pipLeash, @Nullable SurfaceControl.Transaction t, Rect destinationBounds) { @@ -391,8 +401,9 @@ public class PipMenuActivityController { } /** - * Sets the menu actions to the actions provided by the current PiP activity. + * Sets the menu actions to the actions provided by the current PiP menu. */ + @Override public void setAppActions(ParceledListSlice<RemoteAction> appActions) { mAppActions = appActions; updateMenuActions(); @@ -406,10 +417,6 @@ public class PipMenuActivityController { mListeners.forEach(Listener::onPipDismiss); } - void onPipShowMenu() { - mListeners.forEach(Listener::onPipShowMenu); - } - /** * @return the best set of actions to show in the PiP menu. */ @@ -421,21 +428,6 @@ public class PipMenuActivityController { } /** - * Returns a default LayoutParams for the PIP Menu. - * @param width the PIP stack width. - * @param height the PIP stack height. - */ - public static WindowManager.LayoutParams getPipMenuLayoutParams(int width, int height) { - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, - TYPE_APPLICATION_OVERLAY, - FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY | FLAG_NOT_TOUCHABLE, - PixelFormat.TRANSLUCENT); - lp.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY; - lp.setTitle(MENU_WINDOW_TITLE); - return lp; - } - - /** * Updates the PiP menu with the best set of actions provided. */ private void updateMenuActions() { @@ -521,7 +513,7 @@ public class PipMenuActivityController { } } - public void dump(PrintWriter pw, String prefix) { + void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.println(innerPrefix + "mMenuState=" + mMenuState); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java index f153aa5a1beb..7194fc70025c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java @@ -20,15 +20,18 @@ import android.content.Context; import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; -import android.os.Handler; import android.os.RemoteException; import android.view.MagnificationSpec; +import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; +import androidx.annotation.BinderThread; + import com.android.wm.shell.R; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; @@ -40,8 +43,7 @@ import java.util.List; * Expose the touch actions to accessibility as if this object were a window with a single view. * That pseudo-view exposes all of the actions this object can perform. */ -public class PipAccessibilityInteractionConnection - extends IAccessibilityInteractionConnection.Stub { +public class PipAccessibilityInteractionConnection { public interface AccessibilityCallbacks { void onAccessibilityShowMenu(); @@ -50,14 +52,15 @@ public class PipAccessibilityInteractionConnection private static final long ACCESSIBILITY_NODE_ID = 1; private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList; - private Context mContext; - private Handler mHandler; + private final Context mContext; + private final ShellExecutor mShellMainExcutor; private final @NonNull PipBoundsState mPipBoundsState; - private PipMotionHelper mMotionHelper; - private PipTaskOrganizer mTaskOrganizer; - private PipSnapAlgorithm mSnapAlgorithm; - private Runnable mUpdateMovementBoundCallback; - private AccessibilityCallbacks mCallbacks; + private final PipMotionHelper mMotionHelper; + private final PipTaskOrganizer mTaskOrganizer; + private final PipSnapAlgorithm mSnapAlgorithm; + private final Runnable mUpdateMovementBoundCallback; + private final AccessibilityCallbacks mCallbacks; + private final IAccessibilityInteractionConnection mConnectionImpl; private final Rect mNormalBounds = new Rect(); private final Rect mExpandedBounds = new Rect(); @@ -69,19 +72,23 @@ public class PipAccessibilityInteractionConnection @NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper, PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm, AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback, - Handler handler) { + ShellExecutor shellMainExcutor) { mContext = context; - mHandler = handler; + mShellMainExcutor = shellMainExcutor; mPipBoundsState = pipBoundsState; mMotionHelper = motionHelper; mTaskOrganizer = taskOrganizer; mSnapAlgorithm = snapAlgorithm; mUpdateMovementBoundCallback = updateMovementBoundCallback; mCallbacks = callbacks; + mConnectionImpl = new PipAccessibilityInteractionConnectionImpl(); + } + + public void register(AccessibilityManager am) { + am.setPictureInPictureActionReplacingConnection(mConnectionImpl); } - @Override - public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, + private void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) { @@ -94,8 +101,7 @@ public class PipAccessibilityInteractionConnection } } - @Override - public void performAccessibilityAction(long accessibilityNodeId, int action, + private void performAccessibilityAction(long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid) { @@ -115,9 +121,7 @@ public class PipAccessibilityInteractionConnection } else { switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: - mHandler.post(() -> { - mCallbacks.onAccessibilityShowMenu(); - }); + mCallbacks.onAccessibilityShowMenu(); result = true; break; case AccessibilityNodeInfo.ACTION_DISMISS: @@ -172,8 +176,7 @@ public class PipAccessibilityInteractionConnection }); } - @Override - public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, + private void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { @@ -185,8 +188,7 @@ public class PipAccessibilityInteractionConnection } } - @Override - public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, + private void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { @@ -198,8 +200,7 @@ public class PipAccessibilityInteractionConnection } } - @Override - public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion, + private void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { // We have no view that can take focus @@ -210,8 +211,7 @@ public class PipAccessibilityInteractionConnection } } - @Override - public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion, + private void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { // We have no view that can take focus @@ -222,16 +222,6 @@ public class PipAccessibilityInteractionConnection } } - @Override - public void clearAccessibilityFocus() { - // We should not be here. - } - - @Override - public void notifyOutsideTouch() { - // Do nothing. - } - /** * Update the normal and expanded bounds so they can be used for Resize. */ @@ -271,4 +261,95 @@ public class PipAccessibilityInteractionConnection mAccessibilityNodeInfoList.add(info); return mAccessibilityNodeInfoList; } + + @BinderThread + private class PipAccessibilityInteractionConnectionImpl + extends IAccessibilityInteractionConnection.Stub { + @Override + public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, + Region bounds, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, + Bundle arguments) throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this + .findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, bounds, + interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec, arguments); + }); + } + + @Override + public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, + Region bounds, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByViewId( + accessibilityNodeId, viewId, bounds, interactionId, callback, flags, + interrogatingPid, interrogatingTid, spec); + }); + } + + @Override + public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, + Region bounds, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByText( + accessibilityNodeId, text, bounds, interactionId, callback, flags, + interrogatingPid, interrogatingTid, spec); + }); + } + + @Override + public void findFocus(long accessibilityNodeId, int focusType, Region bounds, + int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this.findFocus(accessibilityNodeId, focusType, + bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); + }); + } + + @Override + public void focusSearch(long accessibilityNodeId, int direction, Region bounds, + int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this.focusSearch(accessibilityNodeId, + direction, + bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); + }); + } + + @Override + public void performAccessibilityAction(long accessibilityNodeId, int action, + Bundle arguments, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid) throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this.performAccessibilityAction( + accessibilityNodeId, action, arguments, interactionId, callback, flags, + interrogatingPid, interrogatingTid); + }); + } + + @Override + public void clearAccessibilityFocus() throws RemoteException { + // Do nothing + } + + @Override + public void notifyOutsideTouch() throws RemoteException { + // Do nothing + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 598b5d9b5d30..3234ef6ccf66 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -41,7 +41,6 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.view.DisplayInfo; -import android.view.IPinnedStackController; import android.view.WindowManagerGlobal; import android.window.WindowContainerTransaction; @@ -52,7 +51,6 @@ import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.PipInputConsumer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; @@ -92,7 +90,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private boolean mIsInFixedRotation; private Consumer<Boolean> mPinnedStackAnimationRecentsCallback; - protected PipMenuActivityController mMenuController; + protected PhonePipMenuController mMenuController; protected PipTaskOrganizer mPipTaskOrganizer; protected PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener = new PipControllerPinnedStackListener(); @@ -163,63 +161,50 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private class PipControllerPinnedStackListener extends PinnedStackListenerForwarder.PinnedStackListener { @Override - public void onListenerRegistered(IPinnedStackController controller) { - mMainExecutor.execute(() -> mTouchHandler.setPinnedStackController(controller)); - } - - @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - mMainExecutor.execute(() -> { - mPipBoundsState.setImeVisibility(imeVisible, imeHeight); - mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight); - }); + mPipBoundsState.setImeVisibility(imeVisible, imeHeight); + mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight); } @Override public void onMovementBoundsChanged(boolean fromImeAdjustment) { - mMainExecutor.execute(() -> updateMovementBounds(null /* toBounds */, + updateMovementBounds(null /* toBounds */, false /* fromRotation */, fromImeAdjustment, false /* fromShelfAdjustment */, - null /* windowContainerTransaction */)); + null /* windowContainerTransaction */); } @Override public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { - mMainExecutor.execute(() -> mMenuController.setAppActions(actions)); + mMenuController.setAppActions(actions); } @Override public void onActivityHidden(ComponentName componentName) { - mMainExecutor.execute(() -> { - if (componentName.equals(mPipBoundsState.getLastPipComponentName())) { - // The activity was removed, we don't want to restore to the reentry state - // saved for this component anymore. - mPipBoundsState.setLastPipComponentName(null); - } - }); + if (componentName.equals(mPipBoundsState.getLastPipComponentName())) { + // The activity was removed, we don't want to restore to the reentry state + // saved for this component anymore. + mPipBoundsState.setLastPipComponentName(null); + } } @Override public void onDisplayInfoChanged(DisplayInfo displayInfo) { - mMainExecutor.execute(() -> mPipBoundsState.setDisplayInfo(displayInfo)); + mPipBoundsState.setDisplayInfo(displayInfo); } @Override public void onConfigurationChanged() { - mMainExecutor.execute(() -> { - mPipBoundsAlgorithm.onConfigurationChanged(mContext); - mTouchHandler.onConfigurationChanged(); - mPipBoundsState.onConfigurationChanged(); - }); + mPipBoundsAlgorithm.onConfigurationChanged(mContext); + mTouchHandler.onConfigurationChanged(); + mPipBoundsState.onConfigurationChanged(); } @Override public void onAspectRatioChanged(float aspectRatio) { // TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params // change. - mMainExecutor.execute(() -> { - mPipBoundsState.setAspectRatio(aspectRatio); - mTouchHandler.onAspectRatioChanged(); - }); + mPipBoundsState.setAspectRatio(aspectRatio); + mTouchHandler.onAspectRatioChanged(); } } @@ -229,7 +214,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, PipMediaController pipMediaController, - PipMenuActivityController pipMenuActivityController, + PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, @@ -250,7 +235,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipTaskOrganizer = pipTaskOrganizer; mMainExecutor = mainExecutor; mMediaController = pipMediaController; - mMenuController = pipMenuActivityController; + mMenuController = phonePipMenuController; mTouchHandler = pipTouchHandler; mAppOpsListener = pipAppOpsListener; mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(), @@ -632,7 +617,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac public static PipController create(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipMediaController pipMediaController, - PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, + PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) { if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { @@ -641,7 +626,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, - pipBoundsState, pipMediaController, pipMenuActivityController, pipTaskOrganizer, + pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java index 87ddb181dcce..0c64c8ca9b64 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.android.wm.shell.common; +package com.android.wm.shell.pip.phone; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.INPUT_CONSUMER_PIP; -import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import android.os.Binder; import android.os.IBinder; @@ -30,7 +28,6 @@ import android.view.Choreographer; import android.view.IWindowManager; import android.view.InputChannel; import android.view.InputEvent; -import android.view.WindowManagerGlobal; import java.io.PrintWriter; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 6e3cd8e9aa09..2e10fc93cafb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -23,9 +23,9 @@ import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTR import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_FULL; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_NONE; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -105,7 +105,7 @@ public class PipMenuView extends FrameLayout { private int mBetweenActionPaddingLand; private AnimatorSet mMenuContainerAnimator; - private PipMenuActivityController mController; + private PhonePipMenuController mController; private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @@ -127,7 +127,7 @@ public class PipMenuView extends FrameLayout { protected View mTopEndContainer; protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm; - public PipMenuView(Context context, PipMenuActivityController controller) { + public PipMenuView(Context context, PhonePipMenuController controller) { super(context, null, 0); mContext = context; mController = controller; @@ -182,7 +182,7 @@ public class PipMenuView extends FrameLayout { @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) { - mController.onPipShowMenu(); + mController.showMenu(); } return super.performAccessibilityAction(host, action, args); } @@ -475,7 +475,7 @@ public class PipMenuView extends FrameLayout { final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null)); settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); - mContext.startActivityAsUser(settingsIntent, UserHandle.CURRENT); + mContext.startActivityAsUser(settingsIntent, UserHandle.of(topPipActivityInfo.second)); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index 903f7d773896..e32d3b955081 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -70,7 +70,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private final PipTaskOrganizer mPipTaskOrganizer; private @NonNull PipBoundsState mPipBoundsState; - private PipMenuActivityController mMenuController; + private PhonePipMenuController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); @@ -162,7 +162,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, }; public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState, - PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController, + PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController, PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator) { mContext = context; mPipTaskOrganizer = pipTaskOrganizer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java new file mode 100644 index 000000000000..28cbe35745a9 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java @@ -0,0 +1,123 @@ +/* + * 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.wm.shell.pip.phone; + +import android.graphics.Point; +import android.graphics.Rect; + +/** + * Helper class to calculate the new size given two-fingers pinch to resize. + */ +public class PipPinchResizingAlgorithm { + private static final Rect TMP_RECT = new Rect(); + /** + * Given inputs and requirements and current PiP bounds, return the new size. + * + * @param x0 x-coordinate of the primary input. + * @param y0 y-coordinate of the primary input. + * @param x1 x-coordinate of the secondary input. + * @param y1 y-coordinate of the secondary input. + * @param downx0 x-coordinate of the original down point of the primary input. + * @param downy0 y-coordinate of the original down ponit of the primary input. + * @param downx1 x-coordinate of the original down point of the secondary input. + * @param downy1 y-coordinate of the original down point of the secondary input. + * @param currentPipBounds current PiP bounds. + * @param minVisibleWidth minimum visible width. + * @param minVisibleHeight minimum visible height. + * @param maxSize max size. + * @return The new resized PiP bounds, sharing the same center. + */ + public static Rect pinchResize(float x0, float y0, float x1, float y1, + float downx0, float downy0, float downx1, float downy1, Rect currentPipBounds, + int minVisibleWidth, int minVisibleHeight, Point maxSize) { + + int width = currentPipBounds.width(); + int height = currentPipBounds.height(); + int left = currentPipBounds.left; + int top = currentPipBounds.top; + int right = currentPipBounds.right; + int bottom = currentPipBounds.bottom; + final float aspect = (float) width / (float) height; + final int widthDelta = Math.round(Math.abs(x0 - x1) - Math.abs(downx0 - downx1)); + final int heightDelta = Math.round(Math.abs(y0 - y1) - Math.abs(downy0 - downy1)); + + width = Math.max(minVisibleWidth, Math.min(width + widthDelta, maxSize.x)); + height = Math.max(minVisibleHeight, Math.min(height + heightDelta, maxSize.y)); + + // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major + // drag axis. What ever is producing the bigger rectangle will be chosen. + int width1; + int width2; + int height1; + int height2; + if (aspect > 1.0f) { + // Assuming that the width is our target we calculate the height. + width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width)); + height1 = Math.round((float) width1 / aspect); + if (height1 < minVisibleHeight) { + // If the resulting height is too small we adjust to the minimal size. + height1 = minVisibleHeight; + width1 = Math.max(minVisibleWidth, + Math.min(maxSize.x, Math.round((float) height1 * aspect))); + } + // Assuming that the height is our target we calculate the width. + height2 = Math.max(minVisibleHeight, Math.min(maxSize.y, height)); + width2 = Math.round((float) height2 * aspect); + if (width2 < minVisibleWidth) { + // If the resulting width is too small we adjust to the minimal size. + width2 = minVisibleWidth; + height2 = Math.max(minVisibleHeight, + Math.min(maxSize.y, Math.round((float) width2 / aspect))); + } + } else { + // Assuming that the width is our target we calculate the height. + width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width)); + height1 = Math.round((float) width1 * aspect); + if (height1 < minVisibleHeight) { + // If the resulting height is too small we adjust to the minimal size. + height1 = minVisibleHeight; + width1 = Math.max(minVisibleWidth, + Math.min(maxSize.x, Math.round((float) height1 / aspect))); + } + // Assuming that the height is our target we calculate the width. + height2 = Math.max(minVisibleHeight, Math.min(maxSize.y, height)); + width2 = Math.round((float) height2 / aspect); + if (width2 < minVisibleWidth) { + // If the resulting width is too small we adjust to the minimal size. + width2 = minVisibleWidth; + height2 = Math.max(minVisibleHeight, + Math.min(maxSize.y, Math.round((float) width2 * aspect))); + } + } + + // Use the bigger of the two rectangles if the major change was positive, otherwise + // do the opposite. + final boolean grows = width > (right - left) || height > (bottom - top); + if (grows == (width1 * height1 > width2 * height2)) { + width = width1; + height = height1; + } else { + width = width2; + height = height2; + } + + TMP_RECT.set(currentPipBounds.centerX() - width / 2, + currentPipBounds.centerY() - height / 2, + currentPipBounds.centerX() + width / 2, + currentPipBounds.centerY() + height / 2); + return TMP_RECT; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index e5a49c430d82..88a11689b90b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -39,7 +39,6 @@ import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.InputMonitor; import android.view.MotionEvent; -import android.view.ScaleGestureDetector; import android.view.ViewConfiguration; import androidx.annotation.VisibleForTesting; @@ -62,19 +61,21 @@ import java.util.function.Function; public class PipResizeGestureHandler { private static final String TAG = "PipResizeGestureHandler"; - private static final float PINCH_THRESHOLD = 0.05f; - private static final float STARTING_SCALE_FACTOR = 1.0f; + private static final int PINCH_RESIZE_SNAP_DURATION = 250; private final Context mContext; private final PipBoundsAlgorithm mPipBoundsAlgorithm; private final PipMotionHelper mMotionHelper; private final PipBoundsState mPipBoundsState; + private final PipTaskOrganizer mPipTaskOrganizer; + private final PhonePipMenuController mPhonePipMenuController; + private final PipUiEventLogger mPipUiEventLogger; private final int mDisplayId; private final Executor mMainExecutor; - private final ScaleGestureDetector mScaleGestureDetector; private final Region mTmpRegion = new Region(); private final PointF mDownPoint = new PointF(); + private final PointF mDownSecondaryPoint = new PointF(); private final Point mMaxSize = new Point(); private final Point mMinSize = new Point(); private final Rect mLastResizeBounds = new Rect(); @@ -88,6 +89,7 @@ public class PipResizeGestureHandler { private final Rect mDisplayBounds = new Rect(); private final Function<Rect, Rect> mMovementBoundsSupplier; private final Runnable mUpdateMovementBoundsRunnable; + private final Handler mHandler; private int mDelta; private float mTouchSlop; @@ -96,15 +98,17 @@ public class PipResizeGestureHandler { private boolean mIsEnabled; private boolean mEnablePinchResize; private boolean mIsSysUiStateValid; + // For drag-resize private boolean mThresholdCrossed; + // For pinch-resize + private boolean mThresholdCrossed0; + private boolean mThresholdCrossed1; private boolean mUsingPinchToZoom = false; - private float mScaleFactor = STARTING_SCALE_FACTOR; + int mFirstIndex = -1; + int mSecondIndex = -1; private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; - private PipTaskOrganizer mPipTaskOrganizer; - private PipMenuActivityController mPipMenuActivityController; - private PipUiEventLogger mPipUiEventLogger; private int mCtrlType; @@ -112,7 +116,7 @@ public class PipResizeGestureHandler { PipBoundsState pipBoundsState, PipMotionHelper motionHelper, PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger, - PipMenuActivityController menuActivityController) { + PhonePipMenuController menuActivityController) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); @@ -122,68 +126,13 @@ public class PipResizeGestureHandler { mPipTaskOrganizer = pipTaskOrganizer; mMovementBoundsSupplier = movementBoundsSupplier; mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; - mPipMenuActivityController = menuActivityController; + mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; + mHandler = new Handler(Looper.getMainLooper()); context.getDisplay().getRealSize(mMaxSize); reloadResources(); - mScaleGestureDetector = new ScaleGestureDetector(context, - new ScaleGestureDetector.OnScaleGestureListener() { - @Override - public boolean onScale(ScaleGestureDetector detector) { - mScaleFactor *= detector.getScaleFactor(); - - if (!mThresholdCrossed - && (mScaleFactor > (STARTING_SCALE_FACTOR + PINCH_THRESHOLD) - || mScaleFactor < (STARTING_SCALE_FACTOR - PINCH_THRESHOLD))) { - mThresholdCrossed = true; - mInputMonitor.pilferPointers(); - } - if (mThresholdCrossed) { - int height = Math.min(mMaxSize.y, Math.max(mMinSize.y, - (int) (mScaleFactor * mLastDownBounds.height()))); - int width = Math.min(mMaxSize.x, Math.max(mMinSize.x, - (int) (mScaleFactor * mLastDownBounds.width()))); - int top, bottom, left, right; - - if ((mCtrlType & CTRL_TOP) != 0) { - top = mLastDownBounds.bottom - height; - bottom = mLastDownBounds.bottom; - } else { - top = mLastDownBounds.top; - bottom = mLastDownBounds.top + height; - } - - if ((mCtrlType & CTRL_LEFT) != 0) { - left = mLastDownBounds.right - width; - right = mLastDownBounds.right; - } else { - left = mLastDownBounds.left; - right = mLastDownBounds.left + width; - } - - mLastResizeBounds.set(left, top, right, bottom); - mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, - mLastResizeBounds, - null); - } - return true; - } - - @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - setCtrlTypeForPinchToZoom(); - return true; - } - - @Override - public void onScaleEnd(ScaleGestureDetector detector) { - mScaleFactor = STARTING_SCALE_FACTOR; - finishResize(); - } - }); - mEnablePinchResize = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, PIP_PINCH_RESIZE, @@ -274,7 +223,7 @@ public class PipResizeGestureHandler { if (ev instanceof MotionEvent) { if (mUsingPinchToZoom) { - mScaleGestureDetector.onTouchEvent((MotionEvent) ev); + onPinchResize((MotionEvent) ev); } else { onDragCornerResize((MotionEvent) ev); } @@ -282,6 +231,13 @@ public class PipResizeGestureHandler { } /** + * Checks if there is currently an on-going gesture, either drag-resize or pinch-resize. + */ + public boolean hasOngoingGesture() { + return mCtrlType != CTRL_NONE || mUsingPinchToZoom; + } + + /** * Check whether the current x,y coordinate is within the region in which drag-resize should * start. * This consists of 4 small squares on the 4 corners of the PIP window, a quarter of which @@ -295,7 +251,7 @@ public class PipResizeGestureHandler { * |_|_|_________|_|_| * |_|_| |_|_| */ - public boolean isWithinTouchRegion(int x, int y) { + public boolean isWithinDragResizeRegion(int x, int y) { final Rect currentPipBounds = mPipBoundsState.getBounds(); if (currentPipBounds == null) { return false; @@ -327,15 +283,14 @@ public class PipResizeGestureHandler { if (isInValidSysUiState()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: - // Always pass the DOWN event to the ScaleGestureDetector - mScaleGestureDetector.onTouchEvent(ev); - if (isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY())) { + if (isWithinDragResizeRegion((int) ev.getRawX(), (int) ev.getRawY())) { return true; } break; case MotionEvent.ACTION_POINTER_DOWN: if (mEnablePinchResize && ev.getPointerCount() == 2) { + onPinchResize(ev); mUsingPinchToZoom = true; return true; } @@ -348,33 +303,11 @@ public class PipResizeGestureHandler { return false; } - private void setCtrlTypeForPinchToZoom() { - final Rect currentPipBounds = mPipBoundsState.getBounds(); - mLastDownBounds.set(mPipBoundsState.getBounds()); - - Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds); - mDisplayBounds.set(movementBounds.left, - movementBounds.top, - movementBounds.right + currentPipBounds.width(), - movementBounds.bottom + currentPipBounds.height()); - - if (currentPipBounds.left == mDisplayBounds.left) { - mCtrlType |= CTRL_RIGHT; - } else { - mCtrlType |= CTRL_LEFT; - } - - if (currentPipBounds.top > mDisplayBounds.top + mDisplayBounds.height()) { - mCtrlType |= CTRL_TOP; - } else { - mCtrlType |= CTRL_BOTTOM; - } - } - private void setCtrlType(int x, int y) { final Rect currentPipBounds = mPipBoundsState.getBounds(); Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds); + mDisplayBounds.set(movementBounds.left, movementBounds.top, movementBounds.right + currentPipBounds.width(), @@ -408,6 +341,78 @@ public class PipResizeGestureHandler { return mIsSysUiStateValid; } + private void onPinchResize(MotionEvent ev) { + int action = ev.getActionMasked(); + + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + mFirstIndex = -1; + mSecondIndex = -1; + finishResize(); + } + + if (ev.getPointerCount() != 2) { + return; + } + + if (action == MotionEvent.ACTION_POINTER_DOWN) { + if (mFirstIndex == -1 && mSecondIndex == -1) { + mFirstIndex = 0; + mSecondIndex = 1; + mLastResizeBounds.setEmpty(); + mDownPoint.set(ev.getRawX(mFirstIndex), ev.getRawY(mFirstIndex)); + mDownSecondaryPoint.set(ev.getRawX(mSecondIndex), ev.getRawY(mSecondIndex)); + + mLastResizeBounds.setEmpty(); + mLastDownBounds.set(mPipBoundsState.getBounds()); + } + } + + if (action == MotionEvent.ACTION_MOVE) { + if (mFirstIndex == -1 || mSecondIndex == -1) { + return; + } + + float x0 = ev.getRawX(mFirstIndex); + float y0 = ev.getRawY(mFirstIndex); + float x1 = ev.getRawX(mSecondIndex); + float y1 = ev.getRawY(mSecondIndex); + + double hypot0 = Math.hypot(x0 - mDownPoint.x, y0 - mDownPoint.y); + double hypot1 = Math.hypot(x1 - mDownSecondaryPoint.x, y1 - mDownSecondaryPoint.y); + // Capture inputs + if (hypot0 > mTouchSlop && !mThresholdCrossed0) { + mInputMonitor.pilferPointers(); + mThresholdCrossed0 = true; + // Reset the down to begin resizing from this point + mDownPoint.set(x0, y0); + } + if (hypot1 > mTouchSlop && !mThresholdCrossed1) { + mInputMonitor.pilferPointers(); + mThresholdCrossed1 = true; + // Reset the down to begin resizing from this point + mDownSecondaryPoint.set(x1, y1); + } + if (mThresholdCrossed0 || mThresholdCrossed1) { + if (mPhonePipMenuController.isMenuVisible()) { + mPhonePipMenuController.hideMenu(); + } + + x0 = mThresholdCrossed0 ? x0 : mDownPoint.x; + y0 = mThresholdCrossed0 ? y0 : mDownPoint.y; + x1 = mThresholdCrossed1 ? x1 : mDownSecondaryPoint.x; + y1 = mThresholdCrossed1 ? y1 : mDownSecondaryPoint.y; + + final Rect currentPipBounds = mPipBoundsState.getBounds(); + mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1, + mDownPoint.x, mDownPoint.y, mDownSecondaryPoint.x, mDownSecondaryPoint.y, + currentPipBounds, mMinSize.x, mMinSize.y, mMaxSize)); + + mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds, + null); + } + } + } + private void onDragCornerResize(MotionEvent ev) { int action = ev.getActionMasked(); float x = ev.getX(); @@ -415,15 +420,15 @@ public class PipResizeGestureHandler { if (action == MotionEvent.ACTION_DOWN) { final Rect currentPipBounds = mPipBoundsState.getBounds(); mLastResizeBounds.setEmpty(); - mAllowGesture = isInValidSysUiState() && isWithinTouchRegion((int) x, (int) y); + mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y); if (mAllowGesture) { setCtrlType((int) x, (int) y); mDownPoint.set(x, y); mLastDownBounds.set(mPipBoundsState.getBounds()); } if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY()) - && mPipMenuActivityController.isMenuVisible()) { - mPipMenuActivityController.hideMenu(); + && mPhonePipMenuController.isMenuVisible()) { + mPhonePipMenuController.hideMenu(); } } else if (mAllowGesture) { @@ -442,9 +447,9 @@ public class PipResizeGestureHandler { mInputMonitor.pilferPointers(); } if (mThresholdCrossed) { - if (mPipMenuActivityController.isMenuVisible()) { - mPipMenuActivityController.hideMenuWithoutResize(); - mPipMenuActivityController.hideMenu(); + if (mPhonePipMenuController.isMenuVisible()) { + mPhonePipMenuController.hideMenuWithoutResize(); + mPhonePipMenuController.hideMenu(); } final Rect currentPipBounds = mPipBoundsState.getBounds(); mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y, @@ -468,15 +473,30 @@ public class PipResizeGestureHandler { private void finishResize() { if (!mLastResizeBounds.isEmpty()) { - mUserResizeBounds.set(mLastResizeBounds); - mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, - (Rect bounds) -> { - new Handler(Looper.getMainLooper()).post(() -> { - mMotionHelper.synchronizePinnedStackBounds(); - mUpdateMovementBoundsRunnable.run(); - resetState(); + final Runnable callback = () -> { + mUserResizeBounds.set(mLastResizeBounds); + mMotionHelper.synchronizePinnedStackBounds(); + mUpdateMovementBoundsRunnable.run(); + resetState(); + }; + + // Pinch-to-resize needs to re-calculate snap fraction and animate to the snapped + // position correctly. Drag-resize does not need to move, so just finalize resize. + if (mUsingPinchToZoom) { + final Rect startBounds = new Rect(mLastResizeBounds); + mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, + mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds())); + mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds, + PINCH_RESIZE_SNAP_DURATION, + (Rect rect) -> { + mHandler.post(callback); }); - }); + } else { + mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, + (Rect bounds) -> { + mHandler.post(callback); + }); + } mPipUiEventLogger.log( PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE); } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index a78c4ecdb39f..9281f58f522f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -18,9 +18,9 @@ package com.android.wm.shell.pip.phone; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_FULL; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_NONE; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE; import android.annotation.NonNull; import android.annotation.SuppressLint; @@ -31,11 +31,8 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Handler; -import android.os.RemoteException; import android.provider.DeviceConfig; -import android.util.Log; import android.util.Size; -import android.view.IPinnedStackController; import android.view.InputEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -47,6 +44,7 @@ import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; @@ -75,10 +73,9 @@ public class PipTouchHandler { private final PipDismissTargetHandler mPipDismissTargetHandler; private PipResizeGestureHandler mPipResizeGestureHandler; - private IPinnedStackController mPinnedStackController; private WeakReference<Consumer<Rect>> mPipExclusionBoundsChangeListener; - private final PipMenuActivityController mMenuController; + private final PhonePipMenuController mMenuController; private final AccessibilityManager mAccessibilityManager; private boolean mShowPipMenuOnAnimationEnd = false; @@ -125,7 +122,7 @@ public class PipTouchHandler { /** * A listener for the PIP menu activity. */ - private class PipMenuListener implements PipMenuActivityController.Listener { + private class PipMenuListener implements PhonePipMenuController.Listener { @Override public void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback) { setMenuState(menuState, resize, callback); @@ -152,12 +149,13 @@ public class PipTouchHandler { @SuppressLint("InflateParams") public PipTouchHandler(Context context, - PipMenuActivityController menuController, + PhonePipMenuController menuController, PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, FloatingContentCoordinator floatingContentCoordinator, - PipUiEventLogger pipUiEventLogger) { + PipUiEventLogger pipUiEventLogger, + ShellExecutor shellMainExecutor) { // Initialize the Pip input consumer mContext = context; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); @@ -188,7 +186,7 @@ public class PipTouchHandler { mFloatingContentCoordinator = floatingContentCoordinator; mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState, mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(), - this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler); + this::onAccessibilityShowMenu, this::updateMovementBounds, shellMainExecutor); mPipUiEventLogger = pipUiEventLogger; @@ -436,8 +434,11 @@ public class PipTouchHandler { * TODO Add appropriate description */ public void onRegistrationChanged(boolean isRegistered) { - mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered - ? mConnection : null); + if (isRegistered) { + mConnection.register(mAccessibilityManager); + } else { + mAccessibilityManager.setPictureInPictureActionReplacingConnection(null); + } if (!isRegistered && mTouchState.isUserInteracting()) { // If the input consumer is unregistered while the user is interacting, then we may not // get the final TOUCH_UP event, so clean up the dismiss target as well @@ -459,10 +460,6 @@ public class PipTouchHandler { if (!(inputEvent instanceof MotionEvent)) { return true; } - // Skip touch handling until we are bound to the controller - if (mPinnedStackController == null) { - return true; - } MotionEvent ev = (MotionEvent) inputEvent; if (!mPipBoundsState.isStashed() && mPipResizeGestureHandler.willStartResizeGesture(ev)) { @@ -473,6 +470,11 @@ public class PipTouchHandler { return true; } + if (mPipResizeGestureHandler.hasOngoingGesture()) { + mPipDismissTargetHandler.hideDismissTargetMaybe(); + return true; + } + if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting()) && mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) { // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event @@ -586,13 +588,6 @@ public class PipTouchHandler { } /** - * Sets the controller to update the system of changes from user interaction. - */ - void setPinnedStackController(IPinnedStackController controller) { - mPinnedStackController = controller; - } - - /** * Sets the menu visibility. */ private void setMenuState(int menuState, boolean resize, Runnable callback) { @@ -620,13 +615,9 @@ public class PipTouchHandler { // bounds which are now stale. In such a case we defer the animation to the // normal bounds until after the next onMovementBoundsChanged() call to get the // bounds in the new orientation - try { - int displayRotation = mPinnedStackController.getDisplayRotation(); - if (mDisplayRotation != displayRotation) { - mDeferResizeToNormalBoundsUntilRotation = displayRotation; - } - } catch (RemoteException e) { - Log.e(TAG, "Could not get display rotation from controller"); + int displayRotation = mContext.getDisplay().getRotation(); + if (mDisplayRotation != displayRotation) { + mDeferResizeToNormalBoundsUntilRotation = displayRotation; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java index 217150770084..5f2327ce98d6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java @@ -19,6 +19,7 @@ package com.android.wm.shell.pip.phone; import android.graphics.PointF; import android.os.Handler; import android.util.Log; +import android.view.Display; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; @@ -64,6 +65,7 @@ public class PipTouchState { private boolean mStartedDragging = false; private boolean mAllowDraggingOffscreen = false; private int mActivePointerId; + private int mLastTouchDisplayId = Display.INVALID_DISPLAY; public PipTouchState(ViewConfiguration viewConfig, Handler handler, Runnable doubleTapTimeoutCallback, Runnable hoverExitTimeoutCallback) { @@ -81,12 +83,14 @@ public class PipTouchState { mIsDragging = false; mStartedDragging = false; mIsUserInteracting = false; + mLastTouchDisplayId = Display.INVALID_DISPLAY; } /** * Processes a given touch event and updates the state. */ public void onTouchEvent(MotionEvent ev) { + mLastTouchDisplayId = ev.getDisplayId(); switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: { if (!mAllowTouches) { @@ -266,6 +270,13 @@ public class PipTouchState { } /** + * @return Display ID of the last touch event. + */ + public int getLastTouchDisplayId() { + return mLastTouchDisplayId; + } + + /** * Sets whether touching is currently allowed. */ public void setAllowTouches(boolean allowTouches) { @@ -378,6 +389,7 @@ public class PipTouchState { pw.println(prefix + TAG); pw.println(innerPrefix + "mAllowTouches=" + mAllowTouches); pw.println(innerPrefix + "mActivePointerId=" + mActivePointerId); + pw.println(innerPrefix + "mLastTouchDisplayId=" + mLastTouchDisplayId); pw.println(innerPrefix + "mDownTouch=" + mDownTouch); pw.println(innerPrefix + "mDownDelta=" + mDownDelta); pw.println(innerPrefix + "mLastTouch=" + mLastTouch); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java index 56e97b91c9d2..763370bec1c9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java @@ -36,7 +36,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ParceledListSlice; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Rect; import android.os.Debug; import android.os.Handler; @@ -59,13 +58,14 @@ import com.android.wm.shell.pip.PipTaskOrganizer; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Manages the picture-in-picture (PIP) UI and states. */ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback { - private static final String TAG = "PipController"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final String TAG = "TvPipController"; + static final boolean DEBUG = false; /** * Unknown or invalid state @@ -111,15 +111,13 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private final PipBoundsAlgorithm mPipBoundsAlgorithm; private final PipTaskOrganizer mPipTaskOrganizer; private final PipMediaController mPipMediaController; + private final TvPipMenuController mTvPipMenuController; private IActivityTaskManager mActivityTaskManager; private int mState = STATE_NO_PIP; private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP; private final Handler mHandler = new Handler(); private List<Listener> mListeners = new ArrayList<>(); - private Rect mPipBounds; - private Rect mDefaultPipBounds = new Rect(); - private Rect mMenuModePipBounds; private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; private int mPipTaskId = TASK_ID_NO_PIP; private int mPinnedStackId = INVALID_STACK_ID; @@ -181,46 +179,33 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PinnedStackListenerForwarder.PinnedStackListener { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - mHandler.post(() -> { - mPipBoundsState.setImeVisibility(imeVisible, imeHeight); - if (mState == STATE_PIP) { - if (mImeVisible != imeVisible) { - if (imeVisible) { - // Save the IME height adjustment, and offset to not occlude the IME - mPipBounds.offset(0, -imeHeight); - mImeHeightAdjustment = imeHeight; - } else { - // Apply the inverse adjustment when the IME is hidden - mPipBounds.offset(0, mImeHeightAdjustment); - } - mImeVisible = imeVisible; - resizePinnedStack(STATE_PIP); + mPipBoundsState.setImeVisibility(imeVisible, imeHeight); + if (mState == STATE_PIP) { + if (mImeVisible != imeVisible) { + if (imeVisible) { + // Save the IME height adjustment, and offset to not occlude the IME + mPipBoundsState.getNormalBounds().offset(0, -imeHeight); + mImeHeightAdjustment = imeHeight; + } else { + // Apply the inverse adjustment when the IME is hidden + mPipBoundsState.getNormalBounds().offset(0, mImeHeightAdjustment); } + mImeVisible = imeVisible; + resizePinnedStack(STATE_PIP); } - }); + } } @Override public void onMovementBoundsChanged(boolean fromImeAdjustment) { - mHandler.post(() -> { - mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo()); - - mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds); - mPipBounds.set(mPipBoundsAlgorithm.getNormalBounds()); - if (mDefaultPipBounds.isEmpty()) { - mDefaultPipBounds.set(mPipBoundsAlgorithm.getDefaultBounds()); - } - }); + mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo()); + mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds); } @Override public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { mCustomActions = actions; - mHandler.post(() -> { - for (int i = mListeners.size() - 1; i >= 0; --i) { - mListeners.get(i).onPipMenuActionsChanged(mCustomActions); - } - }); + mTvPipMenuController.setAppActions(mCustomActions); } } @@ -228,6 +213,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipTaskOrganizer pipTaskOrganizer, + TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, PipNotification pipNotification, TaskStackListenerImpl taskStackListener, @@ -237,6 +223,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipNotification = pipNotification; mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipMediaController = pipMediaController; + mTvPipMenuController = tvPipMenuController; + mTvPipMenuController.attachPipController(this); // Ensure that we have the display info in case we get calls to update the bounds // before the listener calls back final DisplayInfo displayInfo = new DisplayInfo(); @@ -289,9 +277,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipController.this.onActivityRestartAttempt(task, clearedTask); } }); - - // TODO(b/169395392) Refactor PipMenuActivity to PipMenuView - PipMenuActivity.setPipController(this); } private void loadConfigurationsAndApply(Configuration newConfig) { @@ -302,14 +287,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac return; } - Resources res = mContext.getResources(); - mMenuModePipBounds = Rect.unflattenFromString(res.getString( - R.string.pip_menu_bounds)); + final Rect menuBounds = Rect.unflattenFromString( + mContext.getResources().getString(R.string.pip_menu_bounds)); + mPipBoundsState.setExpandedBounds(menuBounds); - // Reset the PIP bounds and apply. PIP bounds can be changed by two reasons. - // 1. Configuration changed due to the language change (RTL <-> RTL) - // 2. SystemUI restarts after the crash - mPipBounds = mDefaultPipBounds; resizePinnedStack(getPinnedTaskInfo() == null ? STATE_NO_PIP : STATE_PIP); } @@ -379,16 +360,22 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } private void onActivityPinned(String packageName) { - if (DEBUG) Log.d(TAG, "onActivityPinned()"); - - RootTaskInfo taskInfo = getPinnedTaskInfo(); + final RootTaskInfo taskInfo = getPinnedTaskInfo(); + if (DEBUG) Log.d(TAG, "onActivityPinned, task=" + taskInfo); if (taskInfo == null) { Log.w(TAG, "Cannot find pinned stack"); return; } - if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo); + + // At this point PipBoundsState knows the correct aspect ratio for this pinned task, so we + // use PipBoundsAlgorithm to calculate the normal bounds for the task (PipBoundsAlgorithm + // will query PipBoundsState for the aspect ratio) and pass the bounds over to the + // PipBoundsState. + mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds()); + mPinnedStackId = taskInfo.taskId; mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1]; + // Set state to STATE_PIP so we show it when the pinned stack animation ends. mState = STATE_PIP; mPipMediaController.onActivityPinned(); @@ -434,8 +421,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } if (getState() == STATE_PIP) { - if (mPipBounds != mDefaultPipBounds) { - mPipBounds = mDefaultPipBounds; + if (!Objects.equals(mPipBoundsState.getBounds(), mPipBoundsState.getNormalBounds())) { resizePinnedStack(STATE_PIP); } } @@ -451,7 +437,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac Log.d(TAG, "suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2)); } - mSuspendPipResizingReason |= reason; } @@ -478,6 +463,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac * @param state In Pip state also used to determine the new size for the Pip. */ public void resizePinnedStack(int state) { + if (DEBUG) { Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state=" + getStateDescription(), new Exception()); @@ -509,11 +495,11 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } break; case STATE_PIP_MENU: - newBounds = mMenuModePipBounds; + newBounds = mPipBoundsState.getExpandedBounds(); break; case STATE_PIP: // fallthrough default: - newBounds = mPipBounds; + newBounds = mPipBoundsState.getNormalBounds(); break; } if (newBounds != null) { @@ -544,10 +530,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onShowPipMenu(); } - Intent intent = new Intent(mContext, PipMenuActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(PipMenuActivity.EXTRA_CUSTOM_ACTIONS, mCustomActions); - mContext.startActivity(intent); + + mTvPipMenuController.showMenu(); } /** @@ -650,8 +634,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac void onPipActivityClosed(); /** Invoked when the PIP menu gets shown. */ void onShowPipMenu(); - /** Invoked when the PIP menu actions change. */ - void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions); /** Invoked when the PIPed activity is about to return back to the fullscreen. */ void onMoveToFullscreen(); /** Invoked when we are above to start resizing the Pip. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java index d2270c278161..689c3ede9efa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java @@ -18,109 +18,110 @@ package com.android.wm.shell.pip.tv; import android.animation.Animator; import android.animation.AnimatorInflater; -import android.app.Activity; +import android.annotation.Nullable; import android.app.RemoteAction; -import android.content.Intent; +import android.content.Context; import android.content.pm.ParceledListSlice; -import android.os.Bundle; import android.util.Log; +import android.view.KeyEvent; +import android.view.SurfaceControl; +import android.view.ViewRootImpl; +import android.view.WindowManagerGlobal; +import android.widget.FrameLayout; import com.android.wm.shell.R; import java.util.Collections; /** - * Activity to show the PIP menu to control PIP. - * TODO(b/169395392) Refactor PipMenuActivity to PipMenuView + * The Menu View that shows controls of the PiP. Always fullscreen. */ -public class PipMenuActivity extends Activity implements PipController.Listener { - private static final String TAG = "PipMenuActivity"; +public class PipMenuView extends FrameLayout implements PipController.Listener { + private static final String TAG = "PipMenuView"; private static final boolean DEBUG = PipController.DEBUG; - static final String EXTRA_CUSTOM_ACTIONS = "custom_actions"; - - private static PipController sPipController; - - private Animator mFadeInAnimation; - private Animator mFadeOutAnimation; + private final PipController mPipController; + private final Animator mFadeInAnimation; + private final Animator mFadeOutAnimation; + private final PipControlsViewController mPipControlsViewController; private boolean mRestorePipSizeWhenClose; - private PipControlsViewController mPipControlsViewController; - @Override - protected void onCreate(Bundle bundle) { - if (DEBUG) Log.d(TAG, "onCreate()"); + public PipMenuView(Context context, PipController pipController) { + super(context, null, 0); + mPipController = pipController; + + inflate(context, R.layout.tv_pip_menu, this); - super.onCreate(bundle); - if (sPipController == null) { - finish(); - } - setContentView(R.layout.tv_pip_menu); mPipControlsViewController = new PipControlsViewController( - findViewById(R.id.pip_controls), sPipController); - sPipController.addListener(this); + findViewById(R.id.pip_controls), mPipController); mRestorePipSizeWhenClose = true; mFadeInAnimation = AnimatorInflater.loadAnimator( - this, R.anim.tv_pip_menu_fade_in_animation); + mContext, R.anim.tv_pip_menu_fade_in_animation); mFadeInAnimation.setTarget(mPipControlsViewController.getView()); mFadeOutAnimation = AnimatorInflater.loadAnimator( - this, R.anim.tv_pip_menu_fade_out_animation); + mContext, R.anim.tv_pip_menu_fade_out_animation); mFadeOutAnimation.setTarget(mPipControlsViewController.getView()); - - onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS)); } @Override - protected void onNewIntent(Intent intent) { - if (DEBUG) Log.d(TAG, "onNewIntent(), intent=" + intent); - super.onNewIntent(intent); - - onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS)); + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK + && event.getAction() == KeyEvent.ACTION_UP) { + restorePipAndFinish(); + return true; + } + return super.dispatchKeyEvent(event); } - private void restorePipAndFinish() { - if (DEBUG) Log.d(TAG, "restorePipAndFinish()"); - - if (mRestorePipSizeWhenClose) { - if (DEBUG) Log.d(TAG, " > restoring to the default position"); - - // When PIP menu activity is closed, restore to the default position. - sPipController.resizePinnedStack(PipController.STATE_PIP); + @Nullable + SurfaceControl getWindowSurfaceControl() { + final ViewRootImpl root = getViewRootImpl(); + if (root == null) { + return null; + } + final SurfaceControl out = root.getSurfaceControl(); + if (out != null && out.isValid()) { + return out; } - finish(); + return null; } - @Override - public void onResume() { - if (DEBUG) Log.d(TAG, "onResume()"); - - super.onResume(); + void showMenu() { + mPipController.addListener(this); mFadeInAnimation.start(); + setAlpha(1.0f); + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + getViewRootImpl().getInputToken(), true /* grantFocus */); + } catch (Exception e) { + Log.e(TAG, "Unable to update focus as menu appears", e); + } } - @Override - public void onPause() { - if (DEBUG) Log.d(TAG, "onPause()"); - - super.onPause(); + void hideMenu() { + mPipController.removeListener(this); + mPipController.resumePipResizing( + PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH); mFadeOutAnimation.start(); - restorePipAndFinish(); + setAlpha(0.0f); + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + getViewRootImpl().getInputToken(), false /* grantFocus */); + } catch (Exception e) { + Log.e(TAG, "Unable to update focus as menu disappears", e); + } } - @Override - protected void onDestroy() { - if (DEBUG) Log.d(TAG, "onDestroy()"); - - super.onDestroy(); - sPipController.removeListener(this); - sPipController.resumePipResizing( - PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH); - } + private void restorePipAndFinish() { + if (DEBUG) Log.d(TAG, "restorePipAndFinish()"); - @Override - public void onBackPressed() { - if (DEBUG) Log.d(TAG, "onBackPressed()"); + if (mRestorePipSizeWhenClose) { + if (DEBUG) Log.d(TAG, " > restoring to the default position"); - restorePipAndFinish(); + // When PIP menu activity is closed, restore to the default position. + mPipController.resizePinnedStack(PipController.STATE_PIP); + } + hideMenu(); } @Override @@ -132,11 +133,10 @@ public class PipMenuActivity extends Activity implements PipController.Listener public void onPipActivityClosed() { if (DEBUG) Log.d(TAG, "onPipActivityClosed()"); - finish(); + hideMenu(); } - @Override - public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) { + void setAppActions(ParceledListSlice<RemoteAction> actions) { if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()"); boolean hasCustomActions = actions != null && !actions.getList().isEmpty(); @@ -156,34 +156,15 @@ public class PipMenuActivity extends Activity implements PipController.Listener // Moving PIP to fullscreen is implemented by resizing PINNED_STACK with null bounds. // This conflicts with restoring PIP position, so disable it. mRestorePipSizeWhenClose = false; - finish(); + hideMenu(); } @Override public void onPipResizeAboutToStart() { if (DEBUG) Log.d(TAG, "onPipResizeAboutToStart()"); - finish(); - sPipController.suspendPipResizing( + hideMenu(); + mPipController.suspendPipResizing( PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH); } - - @Override - public void finish() { - if (DEBUG) Log.d(TAG, "finish()", new RuntimeException()); - - super.finish(); - } - - /** - * TODO(b/169395392) Refactor PipMenuActivity to PipMenuView - * - * @param pipController The singleton pipController instance for TV - */ - public static void setPipController(PipController pipController) { - if (sPipController != null) { - return; - } - sPipController = pipController; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java index b30dee4f331f..d56a88874420 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java @@ -19,12 +19,10 @@ package com.android.wm.shell.pip.tv; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.app.RemoteAction; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.graphics.Bitmap; import android.media.MediaMetadata; @@ -99,11 +97,6 @@ public class PipNotification implements PipController.Listener { } @Override - public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) { - // no-op. - } - - @Override public void onMoveToFullscreen() { dismissPipNotification(); mPackageName = null; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java new file mode 100644 index 000000000000..91aef670b946 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -0,0 +1,98 @@ +/* + * 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.wm.shell.pip.tv; + +import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; + +import android.app.RemoteAction; +import android.content.Context; +import android.content.pm.ParceledListSlice; +import android.view.SurfaceControl; + +import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipMenuController; + +/** + * Manages the visibility of the PiP Menu as user interacts with PiP. + */ +public class TvPipMenuController implements PipMenuController { + + private final Context mContext; + private final SystemWindows mSystemWindows; + private final PipBoundsState mPipBoundsState; + private PipMenuView mMenuView; + private PipController mPipController; + private SurfaceControl mLeash; + + public TvPipMenuController(Context context, PipBoundsState pipBoundsState, + SystemWindows systemWindows) { + mContext = context; + mPipBoundsState = pipBoundsState; + mSystemWindows = systemWindows; + } + + void attachPipController(PipController pipController) { + mPipController = pipController; + } + + @Override + public void showMenu() { + if (mMenuView != null) { + mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE, + mPipBoundsState.getDisplayBounds().width(), + mPipBoundsState.getDisplayBounds().height())); + mMenuView.showMenu(); + + // By default, SystemWindows views are above everything else. + // Set the relative z-order so the menu is below PiP. + if (mMenuView.getWindowSurfaceControl() != null && mLeash != null) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setRelativeLayer(mMenuView.getWindowSurfaceControl(), mLeash, -1); + t.apply(); + } + } + } + + @Override + public void attach(SurfaceControl leash) { + if (mMenuView == null) { + mMenuView = new PipMenuView(mContext, mPipController); + mSystemWindows.addView(mMenuView, + getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), + 0, SHELL_ROOT_LAYER_PIP); + mLeash = leash; + } + } + + @Override + public void detach() { + mSystemWindows.removeView(mMenuView); + mMenuView = null; + mLeash = null; + } + + @Override + public void setAppActions(ParceledListSlice<RemoteAction> appActions) { + mMenuView.setAppActions(appActions); + } + + @Override + public boolean isMenuVisible() { + return mMenuView != null && mMenuView.getAlpha() == 1.0f; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index e55f065c1bb2..7c70a4efad91 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -19,6 +19,8 @@ package com.android.wm.shell.splitscreen; import android.graphics.Rect; import android.window.WindowContainerToken; +import com.android.wm.shell.common.annotations.ExternalThread; + import java.io.PrintWriter; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -26,6 +28,7 @@ import java.util.function.Consumer; /** * Interface to engage split screen feature. */ +@ExternalThread public interface SplitScreen { /** Called when keyguard showing state changed. */ void onKeyguardVisibilityChanged(boolean isShowing); diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml index 101b5bf27c77..1054c4345891 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml @@ -21,6 +21,8 @@ <!-- Read and write traces from external storage --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <!-- Write secure settings --> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <!-- Capture screen contents --> @@ -38,7 +40,8 @@ <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> <!-- ATM.removeRootTasksWithActivityTypes() --> <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> - <application> + <!-- Allow the test to write directly to /sdcard/ --> + <application android:requestLegacyExternalStorage="true"> <uses-library android:name="android.test.runner"/> <service android:name=".NotificationListener" diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml index 9dd9f42bdf81..23d7021baffb 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml @@ -34,7 +34,7 @@ <option name="hidden-api-checks" value="false" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> - <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.wm.shell.flicker/files" /> + <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> <option name="clean-up" value="true" /> </metrics_collector> diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml index afb1166415fc..073860875004 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml @@ -34,7 +34,7 @@ <option name="hidden-api-checks" value="false" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> - <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.wm.shell.flicker/files" /> + <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> <option name="clean-up" value="true" /> </metrics_collector> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index 0fb43e263d05..3ed53fb221a7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -18,78 +18,6 @@ package com.android.wm.shell.flicker import com.android.server.wm.flicker.dsl.EventLogAssertion import com.android.server.wm.flicker.dsl.LayersAssertion -import com.android.server.wm.flicker.dsl.WmAssertion -import com.android.server.wm.flicker.helpers.WindowUtils - -@JvmOverloads -fun WmAssertion.statusBarWindowIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("statusBarWindowIsAlwaysVisible", bugId, enabled) { - this.showsAboveAppWindow(FlickerTestBase.STATUS_BAR_WINDOW_TITLE) - } -} - -@JvmOverloads -fun WmAssertion.navBarWindowIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("navBarWindowIsAlwaysVisible", bugId, enabled) { - this.showsAboveAppWindow(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE) - } -} - -@JvmOverloads -fun LayersAssertion.noUncoveredRegions( - beginRotation: Int, - endRotation: Int = beginRotation, - allStates: Boolean = true, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - val startingBounds = WindowUtils.getDisplayBounds(beginRotation) - val endingBounds = WindowUtils.getDisplayBounds(endRotation) - if (allStates) { - all("noUncoveredRegions", bugId, enabled) { - if (startingBounds == endingBounds) { - this.coversAtLeastRegion(startingBounds) - } else { - this.coversAtLeastRegion(startingBounds) - .then() - .coversAtLeastRegion(endingBounds) - } - } - } else { - start("noUncoveredRegions_StartingPos") { - this.coversAtLeastRegion(startingBounds) - } - end("noUncoveredRegions_EndingPos") { - this.coversAtLeastRegion(endingBounds) - } - } -} - -@JvmOverloads -fun LayersAssertion.statusBarLayerIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("statusBarLayerIsAlwaysVisible", bugId, enabled) { - this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE) - } -} - -@JvmOverloads -fun LayersAssertion.navBarLayerIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("navBarLayerIsAlwaysVisible", bugId, enabled) { - this.showsLayer(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE) - } -} @JvmOverloads fun LayersAssertion.appPairsDividerIsVisible( @@ -131,48 +59,6 @@ fun LayersAssertion.dockedStackDividerIsInvisible( } } -@JvmOverloads -fun LayersAssertion.navBarLayerRotatesAndScales( - beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - val startingPos = WindowUtils.getNavigationBarPosition(beginRotation) - val endingPos = WindowUtils.getNavigationBarPosition(endRotation) - - start("navBarLayerRotatesAndScales_StartingPos", bugId, enabled) { - this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos) - } - end("navBarLayerRotatesAndScales_EndingPost", bugId, enabled) { - this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, endingPos) - } - - if (startingPos == endingPos) { - all("navBarLayerRotatesAndScales", bugId, enabled) { - this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos) - } - } -} - -@JvmOverloads -fun LayersAssertion.statusBarLayerRotatesScales( - beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - val startingPos = WindowUtils.getStatusBarPosition(beginRotation) - val endingPos = WindowUtils.getStatusBarPosition(endRotation) - - start("statusBarLayerRotatesScales_StartingPos", bugId, enabled) { - this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, startingPos) - } - end("statusBarLayerRotatesScales_EndingPos", bugId, enabled) { - this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, endingPos) - } -} - fun EventLogAssertion.focusChanges( vararg windows: String, bugId: Int = 0, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt index ced99de21a46..7ac91b065fca 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt @@ -29,10 +29,10 @@ import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.TEST_REPETITIONS import com.android.wm.shell.flicker.appPairsDividerIsInvisible import com.android.wm.shell.flicker.appPairsDividerIsVisible -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt index 0663eb344f46..c9396aa7ea63 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt @@ -23,10 +23,10 @@ import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt index 322034ce7688..76aabc1b83cf 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt @@ -24,10 +24,10 @@ import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index 96d98d56e069..a67b3b760c49 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -31,13 +31,13 @@ import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarLayerRotatesAndScales -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.noUncoveredRegions -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerRotatesScales -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.noUncoveredRegions +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt index d20552f0739d..f79b21ff278d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt @@ -28,10 +28,10 @@ import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.ImeAppHelper import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt index c61a0f171714..5570a562a515 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt @@ -25,10 +25,10 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.dockedStackDividerIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt index bf9286980b9a..7c47d1f1b1ae 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt @@ -29,8 +29,8 @@ import com.android.server.wm.flicker.helpers.resizeSplitScreen import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.dockedStackDividerIsInvisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java deleted file mode 100644 index c9d32c4b1f76..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java +++ /dev/null @@ -1,89 +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.wm.shell.apppairs; - -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.res.Configuration; -import android.graphics.Rect; -import android.view.Display; -import android.view.SurfaceControl; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTestCase; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** Tests for {@link AppPairLayout} */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class AppPairLayoutTests extends ShellTestCase { - @Mock SurfaceControl mSurfaceControl; - private Display mDisplay; - private Configuration mConfiguration; - private AppPairLayout mAppPairLayout; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mConfiguration = getConfiguration(false); - mDisplay = mContext.getDisplay(); - mAppPairLayout = new AppPairLayout(mContext, mDisplay, mConfiguration, mSurfaceControl); - } - - @After - @UiThreadTest - public void tearDown() { - mAppPairLayout.release(); - } - - @Test - @UiThreadTest - public void testUpdateConfiguration() { - assertThat(mAppPairLayout.updateConfiguration(getConfiguration(false))).isFalse(); - assertThat(mAppPairLayout.updateConfiguration(getConfiguration(true))).isTrue(); - } - - @Test - @UiThreadTest - public void testInitRelease() { - mAppPairLayout.init(); - assertThat(mAppPairLayout.getDividerLeash()).isNotNull(); - mAppPairLayout.release(); - assertThat(mAppPairLayout.getDividerLeash()).isNull(); - } - - private static Configuration getConfiguration(boolean isLandscape) { - final Configuration configuration = new Configuration(); - configuration.unset(); - configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; - configuration.windowConfiguration.setBounds( - new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160)); - return configuration; - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java index f12648a7f709..8dbc1d56bcc2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java @@ -55,13 +55,13 @@ public class AppPairTests extends ShellTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); + when(mDisplayController.getDisplay(anyInt())).thenReturn( + mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); mController = new TestAppPairsController( mTaskOrganizer, mSyncQueue, mDisplayController); - when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); - when(mDisplayController.getDisplay(anyInt())).thenReturn( - mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); } @After diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java index f8c68d2018da..fada694a4c07 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java @@ -55,14 +55,14 @@ public class AppPairsControllerTests extends ShellTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); + when(mDisplayController.getDisplay(anyInt())).thenReturn( + mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); mController = new TestAppPairsController( mTaskOrganizer, mSyncQueue, mDisplayController); mPool = mController.getPool(); - when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); - when(mDisplayController.getDisplay(anyInt())).thenReturn( - mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); } @After diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java index 8ece913de53f..a3f134ee97ed 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java @@ -18,10 +18,14 @@ package com.android.wm.shell.apppairs; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; @@ -35,7 +39,7 @@ import org.mockito.MockitoAnnotations; /** Tests for {@link AppPairsPool} */ @SmallTest @RunWith(AndroidJUnit4.class) -public class AppPairsPoolTests { +public class AppPairsPoolTests extends ShellTestCase { private TestAppPairsController mController; private TestAppPairsPool mPool; @Mock private SyncTransactionQueue mSyncQueue; @@ -45,6 +49,7 @@ public class AppPairsPoolTests { @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); mController = new TestAppPairsController( mTaskOrganizer, mSyncQueue, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java index 080cddc58a09..5e0d51809d44 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java @@ -20,12 +20,15 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsState.ITYPE_IME; import static android.view.Surface.ROTATION_0; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import android.graphics.Point; +import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; @@ -68,19 +71,31 @@ public class DisplayImeControllerTest { @Test public void reappliesVisibilityToChangedLeash() { verifyZeroInteractions(mT); + mPerDisplay.mImeShowing = true; - mPerDisplay.mImeShowing = false; - mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[] { - new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0)) - }); + mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl()); + assertFalse(mPerDisplay.mImeShowing); verify(mT).hide(any()); mPerDisplay.mImeShowing = true; - mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[] { - new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0)) - }); + mPerDisplay.insetsControlChanged(insetsStateWithIme(true), insetsSourceControl()); + assertTrue(mPerDisplay.mImeShowing); verify(mT).show(any()); } + + private InsetsSourceControl[] insetsSourceControl() { + return new InsetsSourceControl[]{ + new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0)) + }; + } + + private InsetsState insetsStateWithIme(boolean visible) { + InsetsState state = new InsetsState(); + state.addSource(new InsetsSource(ITYPE_IME)); + state.setSourceVisible(ITYPE_IME, visible); + return state; + } + } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java new file mode 100644 index 000000000000..d87f4c60fad4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.split; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.view.SurfaceControl; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.internal.policy.DividerSnapAlgorithm; +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Tests for {@link SplitLayout} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SplitLayoutTests extends ShellTestCase { + @Mock SplitLayout.LayoutChangeListener mLayoutChangeListener; + @Mock SurfaceControl mRootLeash; + private SplitLayout mSplitLayout; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mSplitLayout = new SplitLayout( + mContext, + getConfiguration(false), + mLayoutChangeListener, + mRootLeash); + } + + @Test + @UiThreadTest + public void testUpdateConfiguration() { + assertThat(mSplitLayout.updateConfiguration(getConfiguration(false))).isFalse(); + assertThat(mSplitLayout.updateConfiguration(getConfiguration(true))).isTrue(); + } + + @Test + public void testUpdateDividePosition() { + mSplitLayout.updateDividePosition(anyInt()); + verify(mLayoutChangeListener).onBoundsChanging(any(SplitLayout.class)); + } + + @Test + public void testSetSnapTarget() { + DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0, + DividerSnapAlgorithm.SnapTarget.FLAG_NONE); + mSplitLayout.setSnapTarget(snapTarget); + verify(mLayoutChangeListener).onBoundsChanged(any(SplitLayout.class)); + + // verify it callbacks properly when the snap target indicates dismissing split. + snapTarget = getSnapTarget(0, DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START); + mSplitLayout.setSnapTarget(snapTarget); + verify(mLayoutChangeListener).onSnappedToDismiss(eq(false)); + snapTarget = getSnapTarget(0, DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END); + mSplitLayout.setSnapTarget(snapTarget); + verify(mLayoutChangeListener).onSnappedToDismiss(eq(true)); + } + + private static Configuration getConfiguration(boolean isLandscape) { + final Configuration configuration = new Configuration(); + configuration.unset(); + configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; + configuration.windowConfiguration.setBounds( + new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160)); + return configuration; + } + + private static DividerSnapAlgorithm.SnapTarget getSnapTarget(int position, int flag) { + return new DividerSnapAlgorithm.SnapTarget( + position /* position */, position /* taskPosition */, flag); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java new file mode 100644 index 000000000000..aa0eb2f95ed8 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.split; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.view.SurfaceControl; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Tests for {@link SplitWindowManager} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SplitWindowManagerTests extends ShellTestCase { + @Mock SurfaceControl mSurfaceControl; + @Mock SplitLayout mSplitLayout; + private SplitWindowManager mSplitWindowManager; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + final Configuration configuration = new Configuration(); + configuration.setToDefaults(); + mSplitWindowManager = new SplitWindowManager(mContext, configuration, mSurfaceControl); + when(mSplitLayout.getDividerBounds()).thenReturn( + new Rect(0, 0, configuration.windowConfiguration.getBounds().width(), + configuration.windowConfiguration.getBounds().height())); + } + + @Test + @UiThreadTest + public void testInitRelease() { + mSplitWindowManager.init(mSplitLayout); + assertThat(mSplitWindowManager.getSurfaceControl()).isNotNull(); + mSplitWindowManager.release(); + assertThat(mSplitWindowManager.getSurfaceControl()).isNull(); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java index 55e7a354f4cd..7f280cd124d2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java @@ -79,7 +79,8 @@ public class PipAnimationControllerTest extends ShellTestCase { @Test public void getAnimator_withBounds_returnBoundsAnimator() { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mLeash, new Rect(), new Rect(), null, TRANSITION_DIRECTION_TO_PIP); + .getAnimator(mLeash, new Rect(), new Rect(), new Rect(), null, + TRANSITION_DIRECTION_TO_PIP); assertEquals("Expect ANIM_TYPE_BOUNDS animation", animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS); @@ -87,16 +88,19 @@ public class PipAnimationControllerTest extends ShellTestCase { @Test public void getAnimator_whenSameTypeRunning_updateExistingAnimator() { + final Rect baseValue = new Rect(0, 0, 100, 100); final Rect startValue = new Rect(0, 0, 100, 100); final Rect endValue1 = new Rect(100, 100, 200, 200); final Rect endValue2 = new Rect(200, 200, 300, 300); final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController - .getAnimator(mLeash, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP); + .getAnimator(mLeash, baseValue, startValue, endValue1, null, + TRANSITION_DIRECTION_TO_PIP); oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); oldAnimator.start(); final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController - .getAnimator(mLeash, startValue, endValue2, null, TRANSITION_DIRECTION_TO_PIP); + .getAnimator(mLeash, baseValue, startValue, endValue2, null, + TRANSITION_DIRECTION_TO_PIP); assertEquals("getAnimator with same type returns same animator", oldAnimator, newAnimator); @@ -122,11 +126,13 @@ public class PipAnimationControllerTest extends ShellTestCase { @Test @SuppressWarnings("unchecked") public void pipTransitionAnimator_updateEndValue() { + final Rect baseValue = new Rect(0, 0, 100, 100); final Rect startValue = new Rect(0, 0, 100, 100); final Rect endValue1 = new Rect(100, 100, 200, 200); final Rect endValue2 = new Rect(200, 200, 300, 300); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mLeash, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP); + .getAnimator(mLeash, baseValue, startValue, endValue1, null, + TRANSITION_DIRECTION_TO_PIP); animator.updateEndValue(endValue2); @@ -135,10 +141,12 @@ public class PipAnimationControllerTest extends ShellTestCase { @Test public void pipTransitionAnimator_setPipAnimationCallback() { + final Rect baseValue = new Rect(0, 0, 100, 100); final Rect startValue = new Rect(0, 0, 100, 100); final Rect endValue = new Rect(100, 100, 200, 200); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mLeash, startValue, endValue, null, TRANSITION_DIRECTION_TO_PIP); + .getAnimator(mLeash, baseValue, startValue, endValue, null, + TRANSITION_DIRECTION_TO_PIP); animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); animator.setPipAnimationCallback(mPipAnimationCallback); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 8d3774cee1e0..45e4241d5bc6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -44,7 +44,7 @@ import android.window.WindowContainerToken; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.pip.phone.PipMenuActivityController; +import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; @@ -66,7 +66,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { @Mock private DisplayController mMockdDisplayController; @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; - @Mock private PipMenuActivityController mMenuActivityController; + @Mock private PhonePipMenuController mMockPhonePipMenuController; @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper; @Mock private PipUiEventLogger mMockPipUiEventLogger; @Mock private Optional<SplitScreen> mMockOptionalSplitScreen; @@ -83,9 +83,9 @@ public class PipTaskOrganizerTest extends ShellTestCase { mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(mContext); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState, - mMockPipBoundsAlgorithm, mMenuActivityController, mMockPipSurfaceTransactionHelper, - mMockOptionalSplitScreen, mMockdDisplayController, mMockPipUiEventLogger, - mMockShellTaskOrganizer)); + mMockPipBoundsAlgorithm, mMockPhonePipMenuController, + mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController, + mMockPipUiEventLogger, mMockShellTaskOrganizer)); preparePipTaskOrg(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 88c8eb902a6f..4687d2d9667c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -61,7 +61,7 @@ public class PipControllerTest extends ShellTestCase { private PipController mPipController; @Mock private DisplayController mMockDisplayController; - @Mock private PipMenuActivityController mMockPipMenuActivityController; + @Mock private PhonePipMenuController mMockPhonePipMenuController; @Mock private PipAppOpsListener mMockPipAppOpsListener; @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; @Mock private PipMediaController mMockPipMediaController; @@ -77,7 +77,7 @@ public class PipControllerTest extends ShellTestCase { MockitoAnnotations.initMocks(this); mPipController = new PipController(mContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState, - mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, + mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener, mMockExecutor); doAnswer(invocation -> { @@ -110,7 +110,7 @@ public class PipControllerTest extends ShellTestCase { assertNull(PipController.create(spyContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState, - mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, + mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener, mMockExecutor)); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index abbc681f53fe..4efaebf96c2b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipSnapAlgorithm; @@ -60,7 +61,7 @@ public class PipTouchHandlerTest extends ShellTestCase { private PipTouchHandler mPipTouchHandler; @Mock - private PipMenuActivityController mPipMenuActivityController; + private PhonePipMenuController mPhonePipMenuController; @Mock private PipTaskOrganizer mPipTaskOrganizer; @@ -71,6 +72,9 @@ public class PipTouchHandlerTest extends ShellTestCase { @Mock private PipUiEventLogger mPipUiEventLogger; + @Mock + private ShellExecutor mShellMainExecutor; + private PipBoundsState mPipBoundsState; private PipBoundsAlgorithm mPipBoundsAlgorithm; private PipSnapAlgorithm mPipSnapAlgorithm; @@ -92,9 +96,9 @@ public class PipTouchHandlerTest extends ShellTestCase { mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState); mPipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm(); mPipSnapAlgorithm = new PipSnapAlgorithm(); - mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController, + mPipTouchHandler = new PipTouchHandler(mContext, mPhonePipMenuController, mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer, - mFloatingContentCoordinator, mPipUiEventLogger); + mFloatingContentCoordinator, mPipUiEventLogger, mShellMainExecutor); mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper()); mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler()); mPipTouchHandler.setPipMotionHelper(mMotionHelper); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index cd53217d2924..1ff1978044b9 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -333,6 +333,7 @@ cc_defaults { "jni/YuvToJpegEncoder.cpp", "jni/fonts/Font.cpp", "jni/fonts/FontFamily.cpp", + "jni/fonts/NativeFont.cpp", "jni/text/LineBreaker.cpp", "jni/text/MeasuredText.cpp", "jni/text/TextShaper.cpp", diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index fd18d2f9192d..8b20492543f7 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -20,7 +20,7 @@ namespace android { namespace uirenderer { -const std::string FrameInfoNames[] = { +const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames = { "Flags", "FrameTimelineVsyncId", "IntendedVsync", @@ -42,10 +42,6 @@ const std::string FrameInfoNames[] = { "GpuCompleted", }; -static_assert((sizeof(FrameInfoNames) / sizeof(FrameInfoNames[0])) == - static_cast<int>(FrameInfoIndex::NumIndexes), - "size mismatch: FrameInfoNames doesn't match the enum!"); - static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 19, "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)"); diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index bb875e35f6f7..738246d56d0d 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -21,6 +21,7 @@ #include <cutils/compiler.h> #include <utils/Timers.h> +#include <array> #include <memory.h> #include <string> @@ -60,7 +61,7 @@ enum class FrameInfoIndex { NumIndexes }; -extern const std::string FrameInfoNames[]; +extern const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames; namespace FrameInfoFlags { enum { diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index c174c240ff22..f4c633fbe58f 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -18,6 +18,7 @@ #include "renderstate/RenderState.h" #include "utils/Color.h" +#include "utils/MathUtils.h" namespace android { namespace uirenderer { @@ -52,5 +53,90 @@ SkBlendMode Layer::getMode() const { } } +static inline SkScalar isIntegerAligned(SkScalar x) { + return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON; +} + +// Disable filtering when there is no scaling in screen coordinates and the corners have the same +// fraction (for translate) or zero fraction (for any other rect-to-rect transform). +static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, const SkRect& dstRect) { + if (!matrix.rectStaysRect()) return true; + SkRect dstDevRect = matrix.mapRect(dstRect); + float dstW, dstH; + if (MathUtils::isZero(matrix.getScaleX()) && MathUtils::isZero(matrix.getScaleY())) { + // Has a 90 or 270 degree rotation, although total matrix may also have scale factors + // in m10 and m01. Those scalings are automatically handled by mapRect so comparing + // dimensions is sufficient, but swap width and height comparison. + dstW = dstDevRect.height(); + dstH = dstDevRect.width(); + } else { + // Handle H/V flips or 180 rotation matrices. Axes may have been mirrored, but + // dimensions are still safe to compare directly. + dstW = dstDevRect.width(); + dstH = dstDevRect.height(); + } + if (!(MathUtils::areEqual(dstW, srcRect.width()) && + MathUtils::areEqual(dstH, srcRect.height()))) { + return true; + } + // Device rect and source rect should be integer aligned to ensure there's no difference + // in how nearest-neighbor sampling is resolved. + return !(isIntegerAligned(srcRect.x()) && + isIntegerAligned(srcRect.y()) && + isIntegerAligned(dstDevRect.x()) && + isIntegerAligned(dstDevRect.y())); +} + +void Layer::draw(SkCanvas* canvas) { + GrRecordingContext* context = canvas->recordingContext(); + if (context == nullptr) { + SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface")); + return; + } + SkMatrix layerTransform = getTransform(); + //sk_sp<SkImage> layerImage = getImage(); + const int layerWidth = getWidth(); + const int layerHeight = getHeight(); + if (layerImage) { + SkMatrix textureMatrixInv; + textureMatrixInv = getTexTransform(); + // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed + // use bottom left origin and remove flipV and invert transformations. + SkMatrix flipV; + flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1); + textureMatrixInv.preConcat(flipV); + textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight); + textureMatrixInv.postScale(layerImage->width(), layerImage->height()); + SkMatrix textureMatrix; + if (!textureMatrixInv.invert(&textureMatrix)) { + textureMatrix = textureMatrixInv; + } + + SkMatrix matrix; + matrix = SkMatrix::Concat(layerTransform, textureMatrix); + + SkPaint paint; + paint.setAlpha(getAlpha()); + paint.setBlendMode(getMode()); + paint.setColorFilter(getColorFilter()); + const bool nonIdentityMatrix = !matrix.isIdentity(); + if (nonIdentityMatrix) { + canvas->save(); + canvas->concat(matrix); + } + const SkMatrix& totalMatrix = canvas->getTotalMatrix(); + + SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height()); + if (getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) { + paint.setFilterQuality(kLow_SkFilterQuality); + } + canvas->drawImage(layerImage.get(), 0, 0, &paint); + // restore the original matrix + if (nonIdentityMatrix) { + canvas->restore(); + } + } +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index ea3bfc9e80cb..e99e76299317 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -21,6 +21,7 @@ #include <SkBlendMode.h> #include <SkColorFilter.h> #include <SkColorSpace.h> +#include <SkCanvas.h> #include <SkPaint.h> #include <SkImage.h> #include <SkMatrix.h> @@ -87,6 +88,8 @@ public: inline sk_sp<SkImage> getImage() const { return this->layerImage; } + void draw(SkCanvas* canvas); + protected: RenderState& mRenderState; diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index e1f5abd786bf..0fad2d58cc8a 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -69,6 +69,7 @@ extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); extern int register_android_graphics_fonts_Font(JNIEnv* env); extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); +extern int register_android_graphics_fonts_NativeFont(JNIEnv* env); extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env); extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env); @@ -135,6 +136,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_drawable_VectorDrawable), REG_JNI(register_android_graphics_fonts_Font), REG_JNI(register_android_graphics_fonts_FontFamily), + REG_JNI(register_android_graphics_fonts_NativeFont), REG_JNI(register_android_graphics_pdf_PdfDocument), REG_JNI(register_android_graphics_pdf_PdfEditor), REG_JNI(register_android_graphics_pdf_PdfRenderer), diff --git a/libs/hwui/canvas/CanvasOpRasterizer.cpp b/libs/hwui/canvas/CanvasOpRasterizer.cpp index 25129f641c00..0093c38cf8a8 100644 --- a/libs/hwui/canvas/CanvasOpRasterizer.cpp +++ b/libs/hwui/canvas/CanvasOpRasterizer.cpp @@ -33,7 +33,11 @@ void rasterizeCanvasBuffer(const CanvasOpBuffer& source, SkCanvas* destination) SkMatrix& currentGlobalTransform = globalMatrixStack.emplace_back(SkMatrix::I()); source.for_each([&]<CanvasOpType T>(const CanvasOpContainer<T> * op) { - if constexpr (T == CanvasOpType::BeginZ || T == CanvasOpType::EndZ) { + if constexpr ( + T == CanvasOpType::BeginZ || + T == CanvasOpType::EndZ || + T == CanvasOpType::DrawLayer + ) { // Do beginZ or endZ LOG_ALWAYS_FATAL("TODO"); return; diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h index f9df2f7aa5ba..f0aa7774a6cc 100644 --- a/libs/hwui/canvas/CanvasOpTypes.h +++ b/libs/hwui/canvas/CanvasOpTypes.h @@ -47,14 +47,17 @@ enum class CanvasOpType : int8_t { DrawArc, DrawPaint, DrawPoint, + DrawPoints, DrawPath, DrawLine, + DrawLines, DrawVertices, DrawImage, DrawImageRect, // DrawImageLattice also used to draw 9 patches DrawImageLattice, DrawPicture, + DrawLayer, // TODO: Rest diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h index 8c7113d5d075..242dbdb27362 100644 --- a/libs/hwui/canvas/CanvasOps.h +++ b/libs/hwui/canvas/CanvasOps.h @@ -26,8 +26,10 @@ #include <hwui/Bitmap.h> #include <log/log.h> #include "CanvasProperty.h" +#include "Points.h" #include "CanvasOpTypes.h" +#include "Layer.h" #include <experimental/type_traits> #include <utility> @@ -165,6 +167,22 @@ struct CanvasOp<CanvasOpType::DrawPoint> { }; template <> +struct CanvasOp<CanvasOpType::DrawPoints> { + size_t count; + SkPaint paint; + sk_sp<Points> points; + void draw(SkCanvas* canvas) const { + canvas->drawPoints( + SkCanvas::kPoints_PointMode, + count, + points->data(), + paint + ); + } + ASSERT_DRAWABLE() +}; + +template <> struct CanvasOp<CanvasOpType::DrawRect> { SkRect rect; SkPaint paint; @@ -263,6 +281,22 @@ struct CanvasOp<CanvasOpType::DrawLine> { }; template<> +struct CanvasOp<CanvasOpType::DrawLines> { + size_t count; + SkPaint paint; + sk_sp<Points> points; + void draw(SkCanvas* canvas) const { + canvas->drawPoints( + SkCanvas::kLines_PointMode, + count, + points->data(), + paint + ); + } + ASSERT_DRAWABLE() +}; + +template<> struct CanvasOp<CanvasOpType::DrawVertices> { sk_sp<SkVertices> vertices; SkBlendMode mode; @@ -364,6 +398,11 @@ struct CanvasOp<CanvasOpType::DrawPicture> { } }; +template<> +struct CanvasOp<CanvasOpType::DrawLayer> { + sp<Layer> layer; +}; + // cleanup our macros #undef ASSERT_DRAWABLE diff --git a/libs/hwui/canvas/Points.h b/libs/hwui/canvas/Points.h new file mode 100644 index 000000000000..05e6a7dd5884 --- /dev/null +++ b/libs/hwui/canvas/Points.h @@ -0,0 +1,48 @@ +/* + * 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. + */ +#include <ui/FatVector.h> +#include "SkPoint.h" +#include "SkRefCnt.h" + +/** + * Collection of points that are ref counted and to be used with + * various drawing calls that consume SkPoint as inputs like + * drawLines/drawPoints + */ +class Points: public SkNVRefCnt<SkPoint> { +public: + Points(int size){ + skPoints.resize(size); + } + + Points(std::initializer_list<SkPoint> init): skPoints(init) { } + + SkPoint& operator[](int index) { + return skPoints[index]; + } + + const SkPoint* data() const { + return skPoints.data(); + } + + size_t size() const { + return skPoints.size(); + } +private: + // Initialize the size to contain 2 SkPoints on the stack for optimized + // drawLine calls that require 2 SkPoints for start/end points of the line + android::FatVector<SkPoint, 2> skPoints; +}; diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index f0c77930cbe3..f612bce748ff 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -187,38 +187,6 @@ static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong } // Critical Native -static jlong Font_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { - const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); - - uint64_t result = font->style().weight(); - result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000; - result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32); - result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48); - return result; -} - -// Critical Native -static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) { - const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); - const minikin::FontVariation& var = minikinSkia->GetAxes().at(index); - uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value); - return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary); -} - -// FastNative -static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontHandle) { - const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); - const std::string& filePath = minikinSkia->getFilePath(); - if (filePath.empty()) { - return nullptr; - } - return env->NewStringUTF(filePath.c_str()); -} - -// Critical Native static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle); return reinterpret_cast<jlong>(font->font.get()); @@ -276,9 +244,6 @@ static const JNINativeMethod gFontBuilderMethods[] = { static const JNINativeMethod gFontMethods[] = { { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds }, { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics }, - { "nGetFontInfo", "(J)J", (void*) Font_getFontInfo }, - { "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo }, - { "nGetFontPath", "(J)Ljava/lang/String;", (void*) Font_getFontPath }, { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr }, { "nGetFontBufferAddress", "(J)J", (void*) Font_GetBufferAddress }, }; diff --git a/libs/hwui/jni/fonts/NativeFont.cpp b/libs/hwui/jni/fonts/NativeFont.cpp new file mode 100644 index 000000000000..c5c5d464ccac --- /dev/null +++ b/libs/hwui/jni/fonts/NativeFont.cpp @@ -0,0 +1,125 @@ +/* + * 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. + */ + +#undef LOG_TAG +#define LOG_TAG "Minikin" + +#include "Font.h" +#include "SkData.h" +#include "SkFont.h" +#include "SkFontMetrics.h" +#include "SkFontMgr.h" +#include "SkRefCnt.h" +#include "SkTypeface.h" +#include "GraphicsJNI.h" +#include <nativehelper/ScopedUtfChars.h> +#include "Utils.h" +#include "FontUtils.h" + +#include <hwui/MinikinSkia.h> +#include <hwui/Paint.h> +#include <hwui/Typeface.h> +#include <minikin/FontFamily.h> +#include <minikin/LocaleList.h> +#include <ui/FatVector.h> + +#include <memory> + +namespace android { + +// Critical Native +static jint NativeFont_getFamilyCount(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle) { + Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle); + return tf->fFontCollection->getFamilies().size(); +} + +// Critical Native +static jlong NativeFont_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle, jint index) { + Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle); + return reinterpret_cast<jlong>(tf->fFontCollection->getFamilies()[index].get()); + +} + +// Fast Native +static jstring NativeFont_getLocaleList(JNIEnv* env, jobject, jlong familyHandle) { + minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle); + uint32_t localeListId = family->localeListId(); + return env->NewStringUTF(minikin::getLocaleString(localeListId).c_str()); +} + +// Critical Native +static jint NativeFont_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle) { + minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle); + return family->getNumFonts(); +} + +// Critical Native +static jlong NativeFont_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle, jint index) { + minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle); + return reinterpret_cast<jlong>(family->getFont(index)); +} + +// Critical Native +static jlong NativeFont_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { + const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); + MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); + + uint64_t result = font->style().weight(); + result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000; + result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32); + result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48); + return result; +} + +// Critical Native +static jlong NativeFont_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) { + const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); + MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); + const minikin::FontVariation& var = minikinSkia->GetAxes().at(index); + uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value); + return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary); +} + +// FastNative +static jstring NativeFont_getFontPath(JNIEnv* env, jobject, jlong fontHandle) { + const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); + MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); + const std::string& filePath = minikinSkia->getFilePath(); + if (filePath.empty()) { + return nullptr; + } + return env->NewStringUTF(filePath.c_str()); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gNativeFontMethods[] = { + { "nGetFamilyCount", "(J)I", (void*) NativeFont_getFamilyCount }, + { "nGetFamily", "(JI)J", (void*) NativeFont_getFamily }, + { "nGetLocaleList", "(J)Ljava/lang/String;", (void*) NativeFont_getLocaleList }, + { "nGetFontCount", "(J)I", (void*) NativeFont_getFontCount }, + { "nGetFont", "(JI)J", (void*) NativeFont_getFont }, + { "nGetFontInfo", "(J)J", (void*) NativeFont_getFontInfo }, + { "nGetAxisInfo", "(JI)J", (void*) NativeFont_getAxisInfo }, + { "nGetFontPath", "(J)Ljava/lang/String;", (void*) NativeFont_getFontPath }, +}; + +int register_android_graphics_fonts_NativeFont(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFont", gNativeFontMethods, + NELEM(gNativeFontMethods)); +} + +} // namespace android diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp index f186e55ec2e3..033a5872ead3 100644 --- a/libs/hwui/tests/unit/CanvasOpTests.cpp +++ b/libs/hwui/tests/unit/CanvasOpTests.cpp @@ -245,6 +245,31 @@ TEST(CanvasOp, simpleDrawPoint) { EXPECT_EQ(1, canvas.sumTotalDrawCalls()); } +TEST(CanvasOp, simpleDrawPoints) { + CanvasOpBuffer buffer; + EXPECT_EQ(buffer.size(), 0); + size_t numPts = 3; + auto pts = sk_ref_sp( + new Points({ + {32, 16}, + {48, 48}, + {16, 32} + }) + ); + + buffer.push(CanvasOp<Op::DrawPoints> { + .count = numPts, + .paint = SkPaint{}, + .points = pts + }); + + CallCountingCanvas canvas; + EXPECT_EQ(0, canvas.sumTotalDrawCalls()); + rasterizeCanvasBuffer(buffer, &canvas); + EXPECT_EQ(1, canvas.drawPoints); + EXPECT_EQ(1, canvas.sumTotalDrawCalls()); +} + TEST(CanvasOp, simpleDrawLine) { CanvasOpBuffer buffer; EXPECT_EQ(buffer.size(), 0); @@ -263,6 +288,30 @@ TEST(CanvasOp, simpleDrawLine) { EXPECT_EQ(1, canvas.sumTotalDrawCalls()); } +TEST(CanvasOp, simpleDrawLines) { + CanvasOpBuffer buffer; + EXPECT_EQ(buffer.size(), 0); + size_t numPts = 3; + auto pts = sk_ref_sp( + new Points({ + {32, 16}, + {48, 48}, + {16, 32} + }) + ); + buffer.push(CanvasOp<Op::DrawLines> { + .count = numPts, + .paint = SkPaint{}, + .points = pts + }); + + CallCountingCanvas canvas; + EXPECT_EQ(0, canvas.sumTotalDrawCalls()); + rasterizeCanvasBuffer(buffer, &canvas); + EXPECT_EQ(1, canvas.drawPoints); + EXPECT_EQ(1, canvas.sumTotalDrawCalls()); +} + TEST(CanvasOp, simpleDrawRect) { CanvasOpBuffer buffer; EXPECT_EQ(buffer.size(), 0); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 047b809bc766..604c4a1de8f9 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1509,8 +1509,15 @@ public class LocationManager { } /** - * Removes location updates for the specified {@link LocationListener}. Following this call, - * the listener will not receive any more invocations of any kind. + * Removes all location updates for the specified {@link LocationListener}. The given listener + * is guaranteed not to receive any invocations that <b>happens-after</b> this method is + * invoked. + * + * <p>If the given listener has any batched requests, this method will not flush any incomplete + * location batches before stopping location updates. If you wish to flush any pending locations + * before stopping, you must first call {@link #requestFlush(String, LocationListener, int)} and + * then call this method once the flush is complete. If this method is invoked before the flush + * is complete, you may not receive the flushed locations. * * @param listener listener that no longer needs location updates * @@ -1537,6 +1544,8 @@ public class LocationManager { * Removes location updates for the specified {@link PendingIntent}. Following this call, the * PendingIntent will no longer receive location updates. * + * <p>See {@link #removeUpdates(LocationListener)} for more detail on how this method works. + * * @param pendingIntent pending intent that no longer needs location updates * * @throws IllegalArgumentException if pendingIntent is null diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 39bdf9557595..bd27f6d068bc 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -18,6 +18,7 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.bluetooth.BluetoothCodecConfig; import android.compat.annotation.UnsupportedAppUsage; @@ -1550,9 +1551,11 @@ public class AudioSystem /** @hide returns master balance value in range -1.f -> 1.f, where 0.f is dead center. */ @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static native float getMasterBalance(); /** @hide Changes the audio balance of the device. */ @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static native int setMasterBalance(float balance); // helpers for android.media.AudioManager.getProperty(), see description there for meaning diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 4b11e3231aba..f8311cd580a9 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -4232,8 +4232,10 @@ public class MediaPlayer extends PlayerBase * @see OnRtpRxNoticeListener * * @param listener the listener called after a notice from RTP Rx - * @param handler the {@link Handler} that receives RTP Tx events - * + * @param handler the {@link Handler} that receives RTP Tx events. If null is passed, + * notifications will be posted on the thread that created this MediaPlayer + * instance. If the creating thread does not have a {@link Looper}, then + * notifications will be posted on the main thread. * @hide */ @SystemApi diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index e9bb7f8d6cb8..1da41fb87b40 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -46,6 +46,8 @@ import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; +import com.android.internal.annotations.VisibleForTesting; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -437,7 +439,7 @@ public final class MediaController { } if (mSessionInfo == null) { - Log.w(TAG, "sessionInfo shouldn't be null."); + Log.d(TAG, "sessionInfo is not set."); mSessionInfo = Bundle.EMPTY; } else if (MediaSession.hasCustomParcelable(mSessionInfo)) { Log.w(TAG, "sessionInfo contains custom parcelable. Ignoring."); @@ -514,6 +516,17 @@ public final class MediaController { return success; } + /** + * Gets associated handler for the given callback. + * @hide + */ + @VisibleForTesting + public Handler getHandlerForCallback(Callback cb) { + synchronized (mLock) { + return getHandlerForCallbackLocked(cb); + } + } + private MessageHandler getHandlerForCallbackLocked(Callback cb) { if (cb == null) { throw new IllegalArgumentException("Callback cannot be null"); diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index a9da77230214..da14ee1f3212 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -60,6 +60,7 @@ import com.android.internal.util.FrameworkStatsLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -296,8 +297,8 @@ public class Tuner implements AutoCloseable { private Executor mOnResourceLostListenerExecutor; private Integer mDemuxHandle; - private Map<Integer, Descrambler> mDescramblers = new HashMap<>(); - private List<Filter> mFilters = new ArrayList<>(); + private Map<Integer, WeakReference<Descrambler>> mDescramblers = new HashMap<>(); + private List<WeakReference<Filter>> mFilters = new ArrayList<WeakReference<Filter>>(); private final TunerResourceManager.ResourcesReclaimListener mResourceListener = new TunerResourceManager.ResourcesReclaimListener() { @@ -308,6 +309,7 @@ public class Tuner implements AutoCloseable { .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN); } + releaseAll(); mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST)); } }; @@ -485,18 +487,28 @@ public class Tuner implements AutoCloseable { if (mLnb != null) { mLnb.close(); } - if (!mDescramblers.isEmpty()) { - for (Map.Entry<Integer, Descrambler> d : mDescramblers.entrySet()) { - d.getValue().close(); - mTunerResourceManager.releaseDescrambler(d.getKey(), mClientId); + synchronized (mDescramblers) { + if (!mDescramblers.isEmpty()) { + for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) { + Descrambler descrambler = d.getValue().get(); + if (descrambler != null) { + descrambler.close(); + } + mTunerResourceManager.releaseDescrambler(d.getKey(), mClientId); + } + mDescramblers.clear(); } - mDescramblers.clear(); } - if (!mFilters.isEmpty()) { - for (Filter f : mFilters) { - f.close(); + synchronized (mFilters) { + if (!mFilters.isEmpty()) { + for (WeakReference<Filter> weakFilter : mFilters) { + Filter filter = weakFilter.get(); + if (filter != null) { + filter.close(); + } + } + mFilters.clear(); } - mFilters.clear(); } if (mDemuxHandle != null) { int res = nativeCloseDemux(mDemuxHandle); @@ -610,7 +622,6 @@ public class Tuner implements AutoCloseable { break; } case MSG_RESOURCE_LOST: { - releaseAll(); if (mOnResourceLostListener != null && mOnResourceLostListenerExecutor != null) { mOnResourceLostListenerExecutor.execute( @@ -1183,7 +1194,10 @@ public class Tuner implements AutoCloseable { if (mHandler == null) { mHandler = createEventHandler(); } - mFilters.add(filter); + synchronized (mFilters) { + WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter); + mFilters.add(weakFilter); + } } return filter; } @@ -1351,7 +1365,10 @@ public class Tuner implements AutoCloseable { int handle = descramblerHandle[0]; Descrambler descrambler = nativeOpenDescramblerByHandle(handle); if (descrambler != null) { - mDescramblers.put(handle, descrambler); + synchronized (mDescramblers) { + WeakReference weakDescrambler = new WeakReference<Descrambler>(descrambler); + mDescramblers.put(handle, weakDescrambler); + } } else { mTunerResourceManager.releaseDescrambler(handle, mClientId); } diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java index af1ff01a3ebe..c762da69eedf 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java @@ -28,7 +28,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Frontend status. + * A Frontend Status class that contains the metrics of the active frontend. * * @hide */ @@ -461,7 +461,7 @@ public class FrontendStatus { } /** - * Lock status for Demod. + * Gets if the demod is currently locked or not. */ public boolean isDemodLocked() { if (mIsDemodLocked == null) { @@ -470,7 +470,7 @@ public class FrontendStatus { return mIsDemodLocked; } /** - * Gets Signal to Noise Ratio in thousandths of a deciBel (0.001dB). + * Gets the current Signal to Noise Ratio in thousandths of a deciBel (0.001dB). */ public int getSnr() { if (mSnr == null) { @@ -479,7 +479,7 @@ public class FrontendStatus { return mSnr; } /** - * Gets Bit Error Ratio. + * Gets the current Bit Error Ratio. * * <p>The number of error bit per 1 billion bits. */ @@ -491,7 +491,7 @@ public class FrontendStatus { } /** - * Gets Packages Error Ratio. + * Gets the current Packages Error Ratio. * * <p>The number of error package per 1 billion packages. */ @@ -502,7 +502,7 @@ public class FrontendStatus { return mPer; } /** - * Gets Bit Error Ratio before Forward Error Correction (FEC). + * Gets the current Bit Error Ratio before Forward Error Correction (FEC). * * <p>The number of error bit per 1 billion bits before FEC. */ @@ -513,7 +513,7 @@ public class FrontendStatus { return mPerBer; } /** - * Gets Signal Quality in percent. + * Gets the current Signal Quality in percent. */ public int getSignalQuality() { if (mSignalQuality == null) { @@ -522,7 +522,7 @@ public class FrontendStatus { return mSignalQuality; } /** - * Gets Signal Strength in thousandths of a dBm (0.001dBm). + * Gets the current Signal Strength in thousandths of a dBm (0.001dBm). */ public int getSignalStrength() { if (mSignalStrength == null) { @@ -531,7 +531,7 @@ public class FrontendStatus { return mSignalStrength; } /** - * Gets symbol rate in symbols per second. + * Gets the current symbol rate in symbols per second. */ public int getSymbolRate() { if (mSymbolRate == null) { @@ -540,7 +540,7 @@ public class FrontendStatus { return mSymbolRate; } /** - * Gets Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1 + * Gets the current Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1 * and ETSI EN 302 307-2 V1.1.1. */ @FrontendSettings.InnerFec @@ -551,7 +551,7 @@ public class FrontendStatus { return mInnerFec; } /** - * Gets modulation. + * Gets the currently modulation information. */ @FrontendModulation public int getModulation() { @@ -561,7 +561,7 @@ public class FrontendStatus { return mModulation; } /** - * Gets Spectral Inversion for DVBC. + * Gets the currently Spectral Inversion information for DVBC. */ @FrontendSettings.FrontendSpectralInversion public int getSpectralInversion() { @@ -571,7 +571,7 @@ public class FrontendStatus { return mInversion; } /** - * Gets Power Voltage Type for LNB. + * Gets the current Power Voltage Type for LNB. */ @Lnb.Voltage public int getLnbVoltage() { @@ -581,7 +581,7 @@ public class FrontendStatus { return mLnbVoltage; } /** - * Gets Physical Layer Pipe ID. + * Gets the current Physical Layer Pipe ID. */ public int getPlpId() { if (mPlpId == null) { @@ -599,7 +599,7 @@ public class FrontendStatus { return mIsEwbs; } /** - * Gets Automatic Gain Control value which is normalized from 0 to 255. + * Gets the current Automatic Gain Control value which is normalized from 0 to 255. */ public int getAgc() { if (mAgc == null) { @@ -617,7 +617,7 @@ public class FrontendStatus { return mIsLnaOn; } /** - * Gets Error status by layer. + * Gets the current Error information by layer. */ @NonNull public boolean[] getLayerErrors() { @@ -627,7 +627,7 @@ public class FrontendStatus { return mIsLayerErrors; } /** - * Gets Modulation Error Ratio in thousandths of a deciBel (0.001dB). + * Gets the current Modulation Error Ratio in thousandths of a deciBel (0.001dB). */ public int getMer() { if (mMer == null) { @@ -636,7 +636,7 @@ public class FrontendStatus { return mMer; } /** - * Gets frequency difference in Hz. + * Gets the current frequency difference in Hz. * * <p>Difference between tuning frequency and actual locked frequency. */ @@ -647,7 +647,7 @@ public class FrontendStatus { return mFreqOffset; } /** - * Gets hierarchy Type for DVBT. + * Gets the current hierarchy Type for DVBT. */ @DvbtFrontendSettings.Hierarchy public int getHierarchy() { @@ -657,7 +657,7 @@ public class FrontendStatus { return mHierarchy; } /** - * Gets lock status for RF. + * Gets if the RF is locked or not. */ public boolean isRfLocked() { if (mIsRfLocked == null) { @@ -666,7 +666,7 @@ public class FrontendStatus { return mIsRfLocked; } /** - * Gets an array of PLP status for tuned PLPs for ATSC3 frontend. + * Gets an array of the current tuned PLPs information of ATSC3 frontend. */ @NonNull public Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo() { @@ -677,9 +677,9 @@ public class FrontendStatus { } /** - * Gets an array of extended bit error ratio status. + * Gets an array of the current extended bit error ratio. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull @@ -693,10 +693,9 @@ public class FrontendStatus { } /** - * Gets an array of code rates status. The {@link FrontendSettings.InnerFec} would be used to - * show the code rate. + * Gets an array of the current code rates. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull @@ -711,9 +710,9 @@ public class FrontendStatus { } /** - * Gets bandwidth status. + * Gets the current bandwidth information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @FrontendBandwidth @@ -727,9 +726,9 @@ public class FrontendStatus { } /** - * Gets guard interval status. + * Gets the current guard interval information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @FrontendGuardInterval @@ -743,9 +742,9 @@ public class FrontendStatus { } /** - * Gets tansmission mode status. + * Gets the current transmission mode information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @FrontendTransmissionMode @@ -759,10 +758,10 @@ public class FrontendStatus { } /** - * Gets the Uncorrectable Error Counts of the frontend's Physical Layer Pipe (PLP) since the - * last tune operation. + * Gets the current Uncorrectable Error Counts of the frontend's Physical Layer Pipe (PLP) + * since the last tune operation. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ public int getUec() { @@ -775,9 +774,9 @@ public class FrontendStatus { } /** - * Gets the current DVB-T2 system id status. + * Gets the current DVB-T2 system id. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @IntRange(from = 0, to = 0xffff) @@ -791,10 +790,9 @@ public class FrontendStatus { } /** - * Gets an array of interleaving status. Array value should be within {@link - * FrontendInterleaveMode}. + * Gets an array of the current interleaving mode information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull @@ -809,9 +807,10 @@ public class FrontendStatus { } /** - * Gets an array of the segments status in ISDB-T Specification of all the channels. + * Gets an array of the current segments information in ISDB-T Specification of all the + * channels. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull @@ -828,7 +827,7 @@ public class FrontendStatus { /** * Gets an array of the Transport Stream Data Rate in BPS of the current channel. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull @@ -842,13 +841,13 @@ public class FrontendStatus { } /** - * Gets an array of the extended modulations status. Array value should be withink {@link - * FrontendModulation}. + * Gets an array of the current extended modulations information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull + @FrontendModulation public int[] getExtendedModulations() { TunerVersionChecker.checkHigherOrEqualVersionTo( TunerVersionChecker.TUNER_VERSION_1_1, "getExtendedModulations status"); @@ -859,9 +858,9 @@ public class FrontendStatus { } /** - * Gets roll off status. + * Gets the current roll off information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @FrontendRollOff @@ -875,9 +874,9 @@ public class FrontendStatus { } /** - * Gets is MISO enabled status. + * Gets is MISO enabled or not. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ public boolean isMisoEnabled() { @@ -890,9 +889,9 @@ public class FrontendStatus { } /** - * Gets is the Code Rate of the frontend is linear or not status. + * Gets is the Code Rate of the frontend is linear or not. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ public boolean isLinear() { @@ -905,9 +904,9 @@ public class FrontendStatus { } /** - * Gets is the Short Frames enabled or not status. + * Gets is the Short Frames enabled or not. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ public boolean isShortFramesEnabled() { @@ -920,7 +919,7 @@ public class FrontendStatus { } /** - * Status for each tuning Physical Layer Pipes. + * Information of each tuning Physical Layer Pipes. */ public static class Atsc3PlpTuningInfo { private final int mPlpId; diff --git a/media/jni/android_media_MediaCodecLinearBlock.h b/media/jni/android_media_MediaCodecLinearBlock.h index 8f1d2fa35d70..ae2d3a264abc 100644 --- a/media/jni/android_media_MediaCodecLinearBlock.h +++ b/media/jni/android_media_MediaCodecLinearBlock.h @@ -49,7 +49,14 @@ struct JMediaCodecLinearBlock { if (offset == 0 && size == block.capacity()) { return mBuffer; } - return C2Buffer::CreateLinearBuffer(block.subBlock(offset, size)); + + std::shared_ptr<C2Buffer> buffer = + C2Buffer::CreateLinearBuffer(block.subBlock(offset, size)); + for (const std::shared_ptr<const C2Info> &info : mBuffer->info()) { + std::shared_ptr<C2Param> param = std::move(C2Param::Copy(*info)); + buffer->setInfo(std::static_pointer_cast<C2Info>(param)); + } + return buffer; } if (mBlock) { return C2Buffer::CreateLinearBuffer(mBlock->share(offset, size, C2Fence{})); diff --git a/native/android/Android.bp b/native/android/Android.bp index 7793d6c45d5e..6db4da95f6db 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -36,6 +36,7 @@ cc_library_shared { defaults: ["libandroid_defaults"], srcs: [ + "activity_manager.cpp", "asset_manager.cpp", "choreographer.cpp", "configuration.cpp", @@ -95,6 +96,10 @@ cc_library_shared { include_dirs: ["bionic/libc/dns/include"], + local_include_dirs: [ "include_platform", ], + + export_include_dirs: [ "include_platform", ], + version_script: "libandroid.map.txt", stubs: { symbol_file: "libandroid.map.txt", diff --git a/native/android/activity_manager.cpp b/native/android/activity_manager.cpp new file mode 100644 index 000000000000..82f4569b871e --- /dev/null +++ b/native/android/activity_manager.cpp @@ -0,0 +1,226 @@ +/* + * 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. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "AActivityManager" +#include <utils/Log.h> + +#include <android/activity_manager.h> +#include <binder/ActivityManager.h> + +namespace android { +namespace activitymanager { + +// Global instance of ActivityManager, service is obtained only on first use. +static ActivityManager gAm; +// String tag used with ActivityManager. +static const String16& getTag() { + static String16 tag("libandroid"); + return tag; +} + +struct UidObserver : public BnUidObserver, public virtual IBinder::DeathRecipient { + explicit UidObserver(const AActivityManager_onUidImportance& cb, + int32_t cutpoint, void* cookie) + : mCallback(cb), mImportanceCutpoint(cutpoint), mCookie(cookie), mRegistered(false) {} + bool registerSelf(); + void unregisterSelf(); + + // IUidObserver + void onUidGone(uid_t uid, bool disabled) override; + void onUidActive(uid_t uid) override; + void onUidIdle(uid_t uid, bool disabled) override; + void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq, + int32_t capability) override; + + // IBinder::DeathRecipient implementation + void binderDied(const wp<IBinder>& who) override; + + static int32_t procStateToImportance(int32_t procState); + static int32_t importanceToProcState(int32_t importance); + + AActivityManager_onUidImportance mCallback; + int32_t mImportanceCutpoint; + void* mCookie; + std::mutex mRegisteredLock; + bool mRegistered GUARDED_BY(mRegisteredLock); +}; + +//static +int32_t UidObserver::procStateToImportance(int32_t procState) { + // TODO: remove this after adding Importance to onUidStateChanged callback. + if (procState == ActivityManager::PROCESS_STATE_NONEXISTENT) { + return AACTIVITYMANAGER_IMPORTANCE_GONE; + } else if (procState >= ActivityManager::PROCESS_STATE_HOME) { + return AACTIVITYMANAGER_IMPORTANCE_CACHED; + } else if (procState == ActivityManager::PROCESS_STATE_HEAVY_WEIGHT) { + return AACTIVITYMANAGER_IMPORTANCE_CANT_SAVE_STATE; + } else if (procState >= ActivityManager::PROCESS_STATE_TOP_SLEEPING) { + return AACTIVITYMANAGER_IMPORTANCE_TOP_SLEEPING; + } else if (procState >= ActivityManager::PROCESS_STATE_SERVICE) { + return AACTIVITYMANAGER_IMPORTANCE_SERVICE; + } else if (procState >= ActivityManager::PROCESS_STATE_TRANSIENT_BACKGROUND) { + return AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE; + } else if (procState >= ActivityManager::PROCESS_STATE_IMPORTANT_FOREGROUND) { + return AACTIVITYMANAGER_IMPORTANCE_VISIBLE; + } else if (procState >= ActivityManager::PROCESS_STATE_FOREGROUND_SERVICE) { + return AACTIVITYMANAGER_IMPORTANCE_FOREGROUND_SERVICE; + } else { + return AACTIVITYMANAGER_IMPORTANCE_FOREGROUND; + } +} + +//static +int32_t UidObserver::importanceToProcState(int32_t importance) { + // TODO: remove this after adding Importance to onUidStateChanged callback. + if (importance == AACTIVITYMANAGER_IMPORTANCE_GONE) { + return ActivityManager::PROCESS_STATE_NONEXISTENT; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_CACHED) { + return ActivityManager::PROCESS_STATE_HOME; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_CANT_SAVE_STATE) { + return ActivityManager::PROCESS_STATE_HEAVY_WEIGHT; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_TOP_SLEEPING) { + return ActivityManager::PROCESS_STATE_TOP_SLEEPING; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_SERVICE) { + return ActivityManager::PROCESS_STATE_SERVICE; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE) { + return ActivityManager::PROCESS_STATE_TRANSIENT_BACKGROUND; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_VISIBLE) { + return ActivityManager::PROCESS_STATE_IMPORTANT_FOREGROUND; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_FOREGROUND_SERVICE) { + return ActivityManager::PROCESS_STATE_FOREGROUND_SERVICE; + } else { + return ActivityManager::PROCESS_STATE_TOP; + } +} + + +void UidObserver::onUidGone(uid_t uid, bool disabled __unused) { + std::scoped_lock lock{mRegisteredLock}; + + if (mRegistered && mCallback) { + mCallback(uid, AACTIVITYMANAGER_IMPORTANCE_GONE, mCookie); + } +} + +void UidObserver::onUidActive(uid_t uid __unused) {} + +void UidObserver::onUidIdle(uid_t uid __unused, bool disabled __unused) {} + +void UidObserver::onUidStateChanged(uid_t uid, int32_t procState, + int64_t procStateSeq __unused, + int32_t capability __unused) { + std::scoped_lock lock{mRegisteredLock}; + + if (mRegistered && mCallback) { + mCallback(uid, procStateToImportance(procState), mCookie); + } +} + +void UidObserver::binderDied(const wp<IBinder>& /*who*/) { + // ActivityManager is dead, try to re-register. + { + std::scoped_lock lock{mRegisteredLock}; + // If client already unregistered, don't try to re-register. + if (!mRegistered) { + return; + } + // Clear mRegistered to re-register. + mRegistered = false; + } + registerSelf(); +} + +bool UidObserver::registerSelf() { + std::scoped_lock lock{mRegisteredLock}; + if (mRegistered) { + return true; + } + + status_t res = gAm.linkToDeath(this); + if (res != OK) { + ALOGE("UidObserver: Failed to linkToDeath with ActivityManager (err %d)", res); + return false; + } + + // TODO: it seems only way to get all changes is to set cutoff to PROCESS_STATE_UNKNOWN. + // But there is no equivalent of PROCESS_STATE_UNKNOWN in the UidImportance. + // If mImportanceCutpoint is < 0, use PROCESS_STATE_UNKNOWN instead. + res = gAm.registerUidObserver( + this, + ActivityManager::UID_OBSERVER_GONE | ActivityManager::UID_OBSERVER_PROCSTATE, + (mImportanceCutpoint < 0) ? ActivityManager::PROCESS_STATE_UNKNOWN + : importanceToProcState(mImportanceCutpoint), + getTag()); + if (res != OK) { + ALOGE("UidObserver: Failed to register with ActivityManager (err %d)", res); + gAm.unlinkToDeath(this); + return false; + } + + mRegistered = true; + ALOGV("UidObserver: Registered with ActivityManager"); + return true; +} + +void UidObserver::unregisterSelf() { + std::scoped_lock lock{mRegisteredLock}; + + if (mRegistered) { + gAm.unregisterUidObserver(this); + gAm.unlinkToDeath(this); + mRegistered = false; + } + + ALOGV("UidObserver: Unregistered with ActivityManager"); +} + +} // activitymanager +} // android + +using namespace android; +using namespace activitymanager; + +struct AActivityManager_UidImportanceListener : public UidObserver { +}; + +AActivityManager_UidImportanceListener* AActivityManager_addUidImportanceListener( + AActivityManager_onUidImportance onUidImportance, int32_t importanceCutpoint, void* cookie) { + sp<UidObserver> observer(new UidObserver(onUidImportance, importanceCutpoint, cookie)); + if (observer == nullptr || !observer->registerSelf()) { + return nullptr; + } + observer->incStrong((void *)AActivityManager_addUidImportanceListener); + return static_cast<AActivityManager_UidImportanceListener*>(observer.get()); +} + +void AActivityManager_removeUidImportanceListener( + AActivityManager_UidImportanceListener* listener) { + if (listener != nullptr) { + UidObserver* observer = static_cast<UidObserver*>(listener); + observer->unregisterSelf(); + observer->decStrong((void *)AActivityManager_addUidImportanceListener); + } +} + +bool AActivityManager_isUidActive(uid_t uid) { + return gAm.isUidActive(uid, getTag()); +} + +int32_t AActivityManager_getUidImportance(uid_t uid) { + return UidObserver::procStateToImportance(gAm.getUidProcessState(uid, getTag())); +} + diff --git a/native/android/include_platform/android/activity_manager.h b/native/android/include_platform/android/activity_manager.h new file mode 100644 index 000000000000..0ecd56d512a2 --- /dev/null +++ b/native/android/include_platform/android/activity_manager.h @@ -0,0 +1,176 @@ +/* + * 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. + */ + +#ifndef __AACTIVITYMANAGER_H__ +#define __AACTIVITYMANAGER_H__ + +#include <sys/cdefs.h> +#include <sys/types.h> + +__BEGIN_DECLS + +struct AActivityManager_UidImportanceListener; +typedef struct AActivityManager_UidImportanceListener AActivityManager_UidImportanceListener; + +/** + * Callback interface when Uid Importance has changed for a uid. + * + * This callback will be called on an arbitrary thread. Calls to a given listener will be + * serialized. + * + * @param uid the uid for which the importance has changed. + * @param uidImportance the new uidImportance for the uid. + * @cookie the same cookie when the UidImportanceListener was added. + * + * Introduced in API 31. + */ +typedef void (*AActivityManager_onUidImportance)(uid_t uid, int32_t uidImportance, void* cookie); + +/** + * ActivityManager Uid Importance constants. + * + * Introduced in API 31. + */ +enum { + /** + * Constant for Uid Importance: This process is running the + * foreground UI; that is, it is the thing currently at the top of the screen + * that the user is interacting with. + */ + AACTIVITYMANAGER_IMPORTANCE_FOREGROUND = 100, + + /** + * Constant for Uid Importance: This process is running a foreground + * service, for example to perform music playback even while the user is + * not immediately in the app. This generally indicates that the process + * is doing something the user actively cares about. + */ + AACTIVITYMANAGER_IMPORTANCE_FOREGROUND_SERVICE = 125, + + /** + * Constant for Uid Importance: This process is running something + * that is actively visible to the user, though not in the immediate + * foreground. This may be running a window that is behind the current + * foreground (so paused and with its state saved, not interacting with + * the user, but visible to them to some degree); it may also be running + * other services under the system's control that it inconsiders important. + */ + AACTIVITYMANAGER_IMPORTANCE_VISIBLE = 200, + + /** + * Constant for Uid Importance: This process is not something the user + * is directly aware of, but is otherwise perceptible to them to some degree. + */ + AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE = 230, + + /** + * Constant for Uid Importance: This process contains services + * that should remain running. These are background services apps have + * started, not something the user is aware of, so they may be killed by + * the system relatively freely (though it is generally desired that they + * stay running as long as they want to). + */ + AACTIVITYMANAGER_IMPORTANCE_SERVICE = 300, + + /** + * Constant for Uid Importance: This process is running the foreground + * UI, but the device is asleep so it is not visible to the user. Though the + * system will try hard to keep its process from being killed, in all other + * ways we consider it a kind of cached process, with the limitations that go + * along with that state: network access, running background services, etc. + */ + AACTIVITYMANAGER_IMPORTANCE_TOP_SLEEPING = 325, + + /** + * Constant for Uid Importance: This process is running an + * application that can not save its state, and thus can't be killed + * while in the background. This will be used with apps that have + * {@link android.R.attr#cantSaveState} set on their application tag. + */ + AACTIVITYMANAGER_IMPORTANCE_CANT_SAVE_STATE = 350, + + /** + * Constant for Uid Importance: This process process contains + * cached code that is expendable, not actively running any app components + * we care about. + */ + AACTIVITYMANAGER_IMPORTANCE_CACHED = 400, + + /** + * Constant for Uid Importance: This process does not exist. + */ + AACTIVITYMANAGER_IMPORTANCE_GONE = 1000, +}; + +#if __ANDROID_API__ >= 31 + +/** + * Adds a UidImportanceListener to the ActivityManager. + * + * This API requires android.Manifest.permission.PACKAGE_USAGE_STATS permission. + * + * @param onUidImportance the listener callback that will receive change reports. + * + * @param importanceCutpoint the level of importance in which the caller is interested + * in differences. For example, if AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE is used + * here, you will receive a call each time a uid's importance transitions between being + * <= AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE and > AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE. + * + * @param cookie a cookie that will be passed back to the listener callback. + * + * @return an opaque pointer of AActivityManager_UidImportanceListener, or nullptr + * upon failure. Upon success, the returned AActivityManager_UidImportanceListener pointer + * must be removed and released through AActivityManager_removeUidImportanceListener. + */ +AActivityManager_UidImportanceListener* AActivityManager_addUidImportanceListener( + AActivityManager_onUidImportance onUidImportance, + int32_t importanceCutpoint, + void* cookie) __INTRODUCED_IN(31); + +/** + * Removes a UidImportanceListener that was added with AActivityManager_addUidImportanceListener. + * + * When this returns, it's guaranteed the listener callback will no longer be invoked. + * + * @param listener the UidImportanceListener to be removed. + */ +void AActivityManager_removeUidImportanceListener( + AActivityManager_UidImportanceListener* listener) __INTRODUCED_IN(31); + +/** + * Queries if a uid is currently active. + * + * This API requires android.Manifest.permission.PACKAGE_USAGE_STATS permission. + * + * @return true if the uid is active, false otherwise. + */ +bool AActivityManager_isUidActive(uid_t uid) __INTRODUCED_IN(31); + +/** + * Queries the current Uid Importance value of a uid. + * + * This API requires android.Manifest.permission.PACKAGE_USAGE_STATS permission. + * + * @param uid the uid for which the importance value is queried. + * @return the current uid importance value for uid. + */ +int32_t AActivityManager_getUidImportance(uid_t uid) __INTRODUCED_IN(31); + +#endif // __ANDROID_API__ >= 31 + +__END_DECLS + +#endif // __AACTIVITYMANAGER_H__ diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index eca67bd7d211..8fa3acf502bc 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -1,5 +1,9 @@ LIBANDROID { global: + AActivityManager_addUidImportanceListener; # apex # introduced=31 + AActivityManager_removeUidImportanceListener; # apex # introduced=31 + AActivityManager_isUidActive; # apex # introduced=31 + AActivityManager_getUidImportance; # apex # introduced=31 AAssetDir_close; AAssetDir_getNextFileName; AAssetDir_rewind; diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp new file mode 100644 index 000000000000..1a51218616d2 --- /dev/null +++ b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp @@ -0,0 +1,11 @@ +android_test_helper_app { + name: "UidImportanceHelperApp", + manifest: "HelperAppManifest.xml", + static_libs: ["androidx.test.rules"], + sdk_version: "test_current", + srcs: ["src/**/*.java"], + test_suites: [ + "general-tests", + ], +} + diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/HelperAppManifest.xml b/native/android/tests/activitymanager/UidImportanceHelperApps/HelperAppManifest.xml new file mode 100644 index 000000000000..3583b33756e9 --- /dev/null +++ b/native/android/tests/activitymanager/UidImportanceHelperApps/HelperAppManifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.tests.UidImportanceHelper" + android:versionCode="1" + android:versionName="1.0" > + + <application android:label="UidImportanceHelper"> + <activity android:name="com.android.tests.UidImportanceHelper.MainActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT"/> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> + diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/src/com/android/tests/UidImportanceHelper/MainActivity.java b/native/android/tests/activitymanager/UidImportanceHelperApps/src/com/android/tests/UidImportanceHelper/MainActivity.java new file mode 100644 index 000000000000..db0f2b5ed967 --- /dev/null +++ b/native/android/tests/activitymanager/UidImportanceHelperApps/src/com/android/tests/UidImportanceHelper/MainActivity.java @@ -0,0 +1,107 @@ +/* + * 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.tests.UidImportanceHelper; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +/** + * This is an empty activity for testing the UID policy of media transcoding service. + */ +public class MainActivity extends Activity { + private static final String TAG = "MainActivity"; + + // Called at the start of the full lifetime. + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Initialize Activity and inflate the UI. + } + + // Called after onCreate has finished, use to restore UI state + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + // Restore UI state from the savedInstanceState. + // This bundle has also been passed to onCreate. + // Will only be called if the Activity has been + // killed by the system since it was last visible. + } + + // Called before subsequent visible lifetimes + // for an activity process. + @Override + public void onRestart() { + super.onRestart(); + // Load changes knowing that the Activity has already + // been visible within this process. + } + + // Called at the start of the visible lifetime. + @Override + public void onStart() { + super.onStart(); + // Apply any required UI change now that the Activity is visible. + } + + // Called at the start of the active lifetime. + @Override + public void onResume() { + super.onResume(); + // Resume any paused UI updates, threads, or processes required + // by the Activity but suspended when it was inactive. + } + + // Called to save UI state changes at the + // end of the active lifecycle. + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + // Save UI state changes to the savedInstanceState. + // This bundle will be passed to onCreate and + // onRestoreInstanceState if the process is + // killed and restarted by the run time. + super.onSaveInstanceState(savedInstanceState); + } + + // Called at the end of the active lifetime. + @Override + public void onPause() { + // Suspend UI updates, threads, or CPU intensive processes + // that don't need to be updated when the Activity isn't + // the active foreground Activity. + super.onPause(); + } + + // Called at the end of the visible lifetime. + @Override + public void onStop() { + // Suspend remaining UI updates, threads, or processing + // that aren't required when the Activity isn't visible. + // Persist all edits or state changes + // as after this call the process is likely to be killed. + super.onStop(); + } + + // Sometimes called at the end of the full lifetime. + @Override + public void onDestroy() { + // Clean up any resources including ending threads, + // closing database connections etc. + super.onDestroy(); + } +} diff --git a/native/android/tests/activitymanager/nativeTests/Android.bp b/native/android/tests/activitymanager/nativeTests/Android.bp new file mode 100644 index 000000000000..d4b5015ad8f3 --- /dev/null +++ b/native/android/tests/activitymanager/nativeTests/Android.bp @@ -0,0 +1,39 @@ +cc_test { + name: "ActivityManagerNativeTestCases", + + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + + srcs: ["src/ActivityManagerNativeTest.cpp"], + + shared_libs: [ + "liblog", + "libutils", + "libandroid", + "libbinder", + ], + + static_libs: [ + "libbase", + "libgtest", + ], + stl: "c++_shared", + + test_suites: [ + "general-tests", + ], + + cflags: [ + "-Werror", + "-Wall", + ], + required: [ + "UidImportanceHelperApp", + ], +} diff --git a/native/android/tests/activitymanager/nativeTests/AndroidTest.xml b/native/android/tests/activitymanager/nativeTests/AndroidTest.xml new file mode 100644 index 000000000000..bf6287ad4883 --- /dev/null +++ b/native/android/tests/activitymanager/nativeTests/AndroidTest.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for ActivityManager native test cases"> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + + <!-- Force root to allow registering UidImportanceListener from native test --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"> + <option name="force-root" value="true" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> + </target_preparer> + <!-- Install the helper apk --> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="UidImportanceHelperApp.apk" /> + </target_preparer> + + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="cleanup" value="true" /> + <option name="push" value="ActivityManagerNativeTestCases->/data/local/tmp/ActivityManagerNativeTestCases" /> + <option name="append-bitness" value="true" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="ActivityManagerNativeTestCases" /> + <option name="runtime-hint" value="1m" /> + </test> + + <!-- Controller that will skip the module if a native bridge situation is detected --> + <!-- For example: module wants to run arm and device is x86 --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" /> +</configuration> diff --git a/native/android/tests/activitymanager/nativeTests/src/ActivityManagerNativeTest.cpp b/native/android/tests/activitymanager/nativeTests/src/ActivityManagerNativeTest.cpp new file mode 100644 index 000000000000..75ba0ffb229a --- /dev/null +++ b/native/android/tests/activitymanager/nativeTests/src/ActivityManagerNativeTest.cpp @@ -0,0 +1,139 @@ +/* + * 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. + */ + +#define LOG_TAG "ActivityManagerNativeTest" + +#include <android-base/logging.h> +#include <android/activity_manager.h> +#include <binder/PermissionController.h> +#include <binder/ProcessState.h> +#include <gtest/gtest.h> + +constexpr const char* kTestPackage = "com.android.tests.UidImportanceHelper"; +constexpr const char* kTestActivity = "com.android.tests.UidImportanceHelper.MainActivity"; +constexpr int64_t kEventTimeoutUs = 500000; + +//----------------------------------------------------------------- +class ActivityManagerNativeTest : public ::testing::Test { +protected: + ActivityManagerNativeTest() : mUidObserver(nullptr), mTestAppUid(-1), mLastUidImportance(-1) {} + + virtual ~ActivityManagerNativeTest() {} + + /* Test setup*/ + virtual void SetUp() { android::ProcessState::self()->startThreadPool(); } + + /* Test tear down */ + virtual void TearDown() {} + + bool waitForImportance(int32_t val, int64_t timeoutUs) { + std::unique_lock lock(mLock); + + if (mLastUidImportance != val && timeoutUs > 0) { + mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs)); + } + + return mLastUidImportance == val; + } + + void onUidImportanceChanged(uid_t uid, int32_t uidImportance) { + LOG(ERROR) << "OnUidImportance: uid " << uid << ", importance " << uidImportance; + std::unique_lock lock(mLock); + + if (uid == mTestAppUid) { + mLastUidImportance = uidImportance; + mCondition.notify_one(); + } + } + + static void OnUidImportance(uid_t uid, int32_t uidImportance, void* cookie) { + ActivityManagerNativeTest* owner = reinterpret_cast<ActivityManagerNativeTest*>(cookie); + owner->onUidImportanceChanged(uid, uidImportance); + } + + AActivityManager_UidImportanceListener* mUidObserver; + uid_t mTestAppUid; + std::mutex mLock; + std::condition_variable mCondition; + int32_t mLastUidImportance; +}; + +static bool getUidForPackage(const char* packageName, /*inout*/ uid_t& uid) { + android::PermissionController pc; + uid = pc.getPackageUid(android::String16(packageName), 0); + if (uid <= 0) { + ALOGE("Unknown package: '%s'", packageName); + return false; + } + return true; +} + +struct ShellHelper { + static bool RunCmd(const std::string& cmdStr) { + int ret = system(cmdStr.c_str()); + if (ret != 0) { + LOG(ERROR) << "Failed to run cmd: " << cmdStr << ", exitcode " << ret; + return false; + } + return true; + } + + static bool Start(const char* packageName, const char* activityName) { + return RunCmd("am start -W " + std::string(packageName) + "/" + std::string(activityName) + + " &> /dev/null"); + } + + static bool Stop(const char* packageName) { + return RunCmd("am force-stop " + std::string(packageName)); + } +}; + +//------------------------------------------------------------------------------------------------- +TEST_F(ActivityManagerNativeTest, testUidImportance) { + pid_t selfPid = ::getpid(); + uid_t selfUid = ::getuid(); + + uid_t testAppUid; + EXPECT_TRUE(getUidForPackage(kTestPackage, testAppUid)); + LOG(INFO) << "testUidImportance: uidselfUid" << selfUid << ", selfPid " << selfPid + << ", testAppUid " << testAppUid; + mTestAppUid = testAppUid; + + // Expect the initial UidImportance to be GONE. + EXPECT_FALSE(AActivityManager_isUidActive(testAppUid)); + EXPECT_EQ(AActivityManager_getUidImportance(testAppUid), AACTIVITYMANAGER_IMPORTANCE_GONE); + + mUidObserver = AActivityManager_addUidImportanceListener(&OnUidImportance, + AACTIVITYMANAGER_IMPORTANCE_FOREGROUND, + (void*)this); + EXPECT_TRUE(mUidObserver != nullptr); + + // Start the test activity, and expect to receive UidImportance change to FOREGROUND. + EXPECT_TRUE(ShellHelper::Start(kTestPackage, kTestActivity)); + EXPECT_TRUE(waitForImportance(AACTIVITYMANAGER_IMPORTANCE_FOREGROUND, kEventTimeoutUs)); + EXPECT_TRUE(AActivityManager_isUidActive(testAppUid)); + EXPECT_EQ(AActivityManager_getUidImportance(testAppUid), + AACTIVITYMANAGER_IMPORTANCE_FOREGROUND); + + // Stop the test activity, and expect to receive UidImportance change to GONE. + EXPECT_TRUE(ShellHelper::Stop(kTestPackage)); + EXPECT_TRUE(waitForImportance(AACTIVITYMANAGER_IMPORTANCE_GONE, kEventTimeoutUs)); + EXPECT_FALSE(AActivityManager_isUidActive(testAppUid)); + EXPECT_EQ(AActivityManager_getUidImportance(testAppUid), AACTIVITYMANAGER_IMPORTANCE_GONE); + + AActivityManager_removeUidImportanceListener(mUidObserver); + mUidObserver = nullptr; +} diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 11d1b0a9ef2a..087275e73ee8 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -322,6 +322,11 @@ public class ExternalStorageProvider extends FileSystemProvider { return true; } + if (TextUtils.equals(Environment.DIRECTORY_ANDROID.toLowerCase(), + path.toLowerCase())) { + return true; + } + return false; } catch (IOException e) { throw new IllegalArgumentException( diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java index 12d21cae48d4..1c0e718a17b2 100644 --- a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java +++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java @@ -84,8 +84,9 @@ public class EmergencyNumberUtils { } private List<EmergencyNumber> getPromotedEmergencyNumbers(int categories) { - Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList( - categories); + // TODO(b/171542607): Use platform API when its bug is fixed. + Map<Integer, List<EmergencyNumber>> allLists = filterEmergencyNumbersByCategories( + mTelephonyManager.getEmergencyNumberList(), categories); if (allLists == null || allLists.isEmpty()) { Log.w(TAG, "Unable to retrieve emergency number lists!"); return new ArrayList<>(); @@ -130,4 +131,28 @@ public class EmergencyNumberUtils { } return promotedEmergencyNumberLists.get(SubscriptionManager.getDefaultSubscriptionId()); } + + /** + * Filter emergency numbers with categories. + */ + private Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories( + Map<Integer, List<EmergencyNumber>> emergencyNumberList, int categories) { + Map<Integer, List<EmergencyNumber>> filteredMap = new ArrayMap<>(); + if (emergencyNumberList == null) { + return filteredMap; + } + for (Integer subscriptionId : emergencyNumberList.keySet()) { + List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get( + subscriptionId); + List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>(); + for (EmergencyNumber number : allNumbersForSub) { + if (number.isInEmergencyServiceCategories(categories)) { + numbersForCategoriesPerSub.add(number); + } + } + filteredMap.put( + subscriptionId, numbersForCategoriesPerSub); + } + return filteredMap; + } } diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 9f1c96d88905..22213cba217d 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -995,8 +995,14 @@ <!-- Settings item title for media transcoding settings. [CHAR LIMIT=85] --> <string name="transcode_settings_title">Media transcoding settings</string> - <!-- Settings item title to disable transcoding globally. [CHAR LIMIT=85] --> - <string name="transcode_enable_all">Disable transcoding</string> + <!-- Settings item title to enable user's control over further transcoding preferences. [CHAR LIMIT=85] --> + <string name="transcode_user_control">Override transcoding defaults</string> + + <!-- Settings item title to enable transcoding globally. [CHAR LIMIT=85] --> + <string name="transcode_enable_all">Enable transcoding</string> + + <!-- Settings item title to select the default behavior for transcoding if an encodig is not supported by an app. [CHAR LIMIT=85] --> + <string name="transcode_default">Assume apps support modern formats</string> <!-- Settings category title for selecting apps to be enabled for transcoding. [CHAR LIMIT=85] --> <string name="transcode_skip_apps">Enable transcoding for apps</string> @@ -1110,7 +1116,7 @@ <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration --> <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until charged</string> <!-- [CHAR_LIMIT=40] Label for battery level chart when charge been limited --> - <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Battery limited temporarily</string> + <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Optimizing for battery health</string> <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed --> <string name="battery_info_status_unknown">Unknown</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java index f9bb90e9616a..b3205d7563b2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java @@ -16,6 +16,7 @@ package com.android.settingslib.fuelgauge; +import static android.os.BatteryManager.BATTERY_HEALTH_OVERHEAT; import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; import static android.os.BatteryManager.BATTERY_STATUS_FULL; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; @@ -128,6 +129,15 @@ public class BatteryStatus { } /** + * Whether battery is overheated. + * + * @return true if battery is overheated + */ + public boolean isOverheated() { + return health == BATTERY_HEALTH_OVERHEAT; + } + + /** * Return current chargin speed is fast, slow or normal. * * @return the charing speed diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueValidator.java index 183651f77f5a..e7d870ee75da 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueValidator.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueValidator.java @@ -34,6 +34,6 @@ public final class DiscreteValueValidator implements Validator { @Override public boolean validate(@Nullable String value) { - return ArrayUtils.contains(mValues, value); + return value == null || ArrayUtils.contains(mValues, value); } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index a02d67fd7bca..668e26751b0c 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -34,10 +34,6 @@ import java.util.Map; * Validators for Global settings */ public class GlobalSettingsValidators { - /** - * All settings in {@link Global.SETTINGS_TO_BACKUP} array *must* have a non-null validator, - * otherwise they won't be restored. - */ public static final Map<String, Validator> VALIDATORS = new ArrayMap<>(); static { diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveFloatRangeValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveFloatRangeValidator.java index 1a0b88c1c150..58620b5f4c8f 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveFloatRangeValidator.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveFloatRangeValidator.java @@ -34,6 +34,9 @@ final class InclusiveFloatRangeValidator implements Validator { @Override public boolean validate(@Nullable String value) { + if (value == null) { + return true; + } try { final float floatValue = Float.parseFloat(value); return floatValue >= mMin && floatValue <= mMax; diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveIntegerRangeValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveIntegerRangeValidator.java index f9f8ce851719..aa27c877a786 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveIntegerRangeValidator.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveIntegerRangeValidator.java @@ -34,6 +34,9 @@ final class InclusiveIntegerRangeValidator implements Validator { @Override public boolean validate(@Nullable String value) { + if (value == null) { + return true; + } try { final int intValue = Integer.parseInt(value); return intValue >= mMin && intValue <= mMax; diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 9331b5e09f38..517fa54cd805 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -42,11 +42,6 @@ import java.util.Map; * Validators for the Secure Settings. */ public class SecureSettingsValidators { - /** - * All settings in {@link Secure.SETTINGS_TO_BACKUP} and {@link - * DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP} array *must* have a non-null - * validator, otherwise they won't be restored. - */ public static final Map<String, Validator> VALIDATORS = new ArrayMap<>(); static { diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java index 8d5c6e69b850..97e1d6848af6 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java @@ -16,6 +16,7 @@ package android.provider.settings.validators; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.net.Uri; @@ -48,6 +49,9 @@ public class SettingsValidators { public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() { @Override public boolean validate(@Nullable String value) { + if (value == null) { + return true; + } try { return Integer.parseInt(value) >= 0; } catch (NumberFormatException e) { @@ -59,6 +63,9 @@ public class SettingsValidators { public static final Validator ANY_INTEGER_VALIDATOR = new Validator() { @Override public boolean validate(@Nullable String value) { + if (value == null) { + return true; + } try { Integer.parseInt(value); return true; @@ -71,6 +78,9 @@ public class SettingsValidators { public static final Validator URI_VALIDATOR = new Validator() { @Override public boolean validate(@Nullable String value) { + if (value == null) { + return true; + } try { Uri.decode(value); return true; @@ -87,7 +97,7 @@ public class SettingsValidators { */ public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() { @Override - public boolean validate(@Nullable String value) { + public boolean validate(@NonNull String value) { return value != null && ComponentName.unflattenFromString(value) != null; } }; @@ -104,18 +114,15 @@ public class SettingsValidators { public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() { @Override - public boolean validate(@Nullable String value) { + public boolean validate(@NonNull String value) { return value != null && isStringPackageName(value); } - private boolean isStringPackageName(String value) { + private boolean isStringPackageName(@NonNull String value) { // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers, // and underscores ('_'). However, individual package name parts may only // start with letters. // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package) - if (value == null) { - return false; - } String[] subparts = value.split("\\."); boolean isValidPackageName = true; for (String subpart : subparts) { @@ -143,7 +150,7 @@ public class SettingsValidators { @Override public boolean validate(@Nullable String value) { if (value == null) { - return false; + return true; } return value.length() <= MAX_IPV6_LENGTH; } @@ -153,7 +160,7 @@ public class SettingsValidators { @Override public boolean validate(@Nullable String value) { if (value == null) { - return false; + return true; } Locale[] validLocales = Locale.getAvailableLocales(); for (Locale locale : validLocales) { @@ -167,6 +174,9 @@ public class SettingsValidators { /** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */ public static final Validator JSON_OBJECT_VALIDATOR = (value) -> { + if (value == null) { + return true; + } if (TextUtils.isEmpty(value)) { return false; } @@ -184,6 +194,9 @@ public class SettingsValidators { static final Validator DATE_FORMAT_VALIDATOR = value -> { try { + if (value == null) { + return true; + } new SimpleDateFormat(value); return true; } catch (IllegalArgumentException | NullPointerException e) { @@ -211,6 +224,9 @@ public class SettingsValidators { static final Validator NONE_NEGATIVE_LONG_VALIDATOR = new Validator() { @Override public boolean validate(String value) { + if (value == null) { + return true; + } try { return Long.parseLong(value) >= 0; } catch (NumberFormatException e) { diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index c5d4fa9f1b40..d278c5974ae6 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -38,12 +38,6 @@ import java.util.Map; * Validators for System settings */ public class SystemSettingsValidators { - /** - * These are all public system settings - * - * <p>All settings in {@link System.SETTINGS_TO_BACKUP} array *must* have a non-null validator, - * otherwise they won't be restored. - */ @UnsupportedAppUsage public static final Map<String, Validator> VALIDATORS = new ArrayMap<>(); @@ -89,15 +83,7 @@ public class SystemSettingsValidators { return value == null || value.length() < MAX_LENGTH; } }); - VALIDATORS.put( - System.FONT_SCALE, - value -> { - try { - return Float.parseFloat(value) >= 0; - } catch (NumberFormatException | NullPointerException e) { - return false; - } - }); + VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f)); VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR); VALIDATORS.put( System.DISPLAY_COLOR_MODE, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index b061df1423ba..40b0fcff3aac 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -496,11 +496,14 @@ final class SettingsState { public List<String> setSettingsLocked(String prefix, Map<String, String> keyValues, String packageName) { List<String> changedKeys = new ArrayList<>(); + final Iterator<Map.Entry<String, Setting>> iterator = mSettings.entrySet().iterator(); // Delete old keys with the prefix that are not part of the new set. - for (int i = 0; i < mSettings.keySet().size(); ++i) { - String key = mSettings.keyAt(i); - if (key.startsWith(prefix) && !keyValues.containsKey(key)) { - Setting oldState = mSettings.remove(key); + while (iterator.hasNext()) { + Map.Entry<String, Setting> entry = iterator.next(); + final String key = entry.getKey(); + final Setting oldState = entry.getValue(); + if (key != null && key.startsWith(prefix) && !keyValues.containsKey(key)) { + iterator.remove(); FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key, /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false, diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 48c0dc4fb2b8..c740bac4d0b4 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -146,7 +146,6 @@ public class SettingsBackupTest { Settings.Global.BROADCAST_OFFLOAD_CONSTANTS, Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD, Settings.Global.BATTERY_DISCHARGE_THRESHOLD, - Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS, Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, Settings.Global.BATTERY_STATS_CONSTANTS, Settings.Global.BINDER_CALLS_STATS, @@ -230,9 +229,8 @@ public class SettingsBackupTest { Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV, Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_VR, - Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, + Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH, Settings.Global.DEVICE_DEMO_MODE, - Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS, Settings.Global.BATTERY_SAVER_CONSTANTS, Settings.Global.BATTERY_TIP_CONSTANTS, Settings.Global.DEFAULT_SM_DP_PLUS, diff --git a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java index 9134d875097e..e5d148cb213a 100644 --- a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java +++ b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java @@ -56,8 +56,8 @@ public class SettingsValidatorsTest { } @Test - public void testNonNegativeIntegerValidator_onNullValue_returnsFalse() { - assertFalse(SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR.validate(null)); + public void testNonNegativeIntegerValidator_onNullValue_returnsTrue() { + assertTrue(SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR.validate(null)); } @Test @@ -69,8 +69,8 @@ public class SettingsValidatorsTest { } @Test - public void testAnyIntegerValidator_onNullValue_returnsFalse() { - assertFalse(SettingsValidators.ANY_INTEGER_VALIDATOR.validate(null)); + public void testAnyIntegerValidator_onNullValue_returnsTrue() { + assertTrue(SettingsValidators.ANY_INTEGER_VALIDATOR.validate(null)); } @Test @@ -91,8 +91,8 @@ public class SettingsValidatorsTest { } @Test - public void testLenientIpAddressValidator_onNullValue_returnsFalse() { - assertFalse(SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR.validate(null)); + public void testLenientIpAddressValidator_onNullValue_returnsTrue() { + assertTrue(SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR.validate(null)); } @Test @@ -120,8 +120,8 @@ public class SettingsValidatorsTest { } @Test - public void testLocaleValidator_onNullValue_returnsFalse() { - assertFalse(SettingsValidators.LOCALE_VALIDATOR.validate(null)); + public void testLocaleValidator_onNullValue_returnsTrue() { + assertTrue(SettingsValidators.LOCALE_VALIDATOR.validate(null)); } @Test @@ -149,11 +149,11 @@ public class SettingsValidatorsTest { } @Test - public void testDiscreteValueValidator_onNullValue_returnsFalse() { + public void testDiscreteValueValidator_onNullValue_returnsTrue() { String[] discreteTypes = new String[]{"Type1", "Type2"}; Validator v = new DiscreteValueValidator(discreteTypes); - assertFalse(v.validate(null)); + assertTrue(v.validate(null)); } @Test @@ -167,10 +167,10 @@ public class SettingsValidatorsTest { } @Test - public void testInclusiveIntegerRangeValidator_onNullValue_returnsFalse() { + public void testInclusiveIntegerRangeValidator_onNullValue_returnsTrue() { Validator v = new InclusiveIntegerRangeValidator(0, 5); - assertFalse(v.validate(null)); + assertTrue(v.validate(null)); } @Test @@ -184,10 +184,10 @@ public class SettingsValidatorsTest { } @Test - public void testInclusiveFloatRangeValidator_onNullValue_returnsFalse() { + public void testInclusiveFloatRangeValidator_onNullValue_returnsTrue() { Validator v = new InclusiveFloatRangeValidator(0.0f, 5.0f); - assertFalse(v.validate(null)); + assertTrue(v.validate(null)); } @Test @@ -220,8 +220,8 @@ public class SettingsValidatorsTest { } @Test - public void dateFormatValidator_onNullValue_returnsFalse() { - assertFalse(SettingsValidators.DATE_FORMAT_VALIDATOR.validate(null)); + public void dateFormatValidator_onNullValue_returnsTrue() { + assertTrue(SettingsValidators.DATE_FORMAT_VALIDATOR.validate(null)); } @Test @@ -240,8 +240,8 @@ public class SettingsValidatorsTest { } @Test - public void testJSONObjectValidator_onNullValue_returnsFalse() { - assertFalse(SettingsValidators.JSON_OBJECT_VALIDATOR.validate(null)); + public void testJSONObjectValidator_onNullValue_returnsTrue() { + assertTrue(SettingsValidators.JSON_OBJECT_VALIDATOR.validate(null)); } @Test diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 2e3ea24f62e2..141b8cbe8022 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -332,6 +332,9 @@ <!-- Permission needed for CTS test - DisplayTest --> <uses-permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" /> + <!-- Permission needed for CTS test - MatchContentFrameRateTest --> + <uses-permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE" /> + <!-- Permission needed for CTS test - TimeManagerTest --> <uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" /> @@ -344,9 +347,18 @@ <!-- Permissions required for CTS test - NotificationManagerTest --> <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" /> + <!-- Permissions required for CTS test - CtsContactsProviderTestCases --> + <uses-permission android:name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" /> + + <!-- Permissions required for CTS test - CarrierMessagingServiceWrapprTest --> + <uses-permission android:name="android.permission.BIND_CARRIER_SERVICES" /> + <!-- Allows overriding the system's device state from the shell --> <uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/> + <!-- Permissions required for CTS tests to close system dialogs --> + <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 52b41a43c63e..2da958f6b8b9 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -516,20 +516,6 @@ android:excludeFromRecents="true" android:visibleToInstantApps="true"/> - <!-- started from PipController --> - <activity - android:name="com.android.wm.shell.pip.tv.PipMenuActivity" - android:permission="com.android.systemui.permission.SELF" - android:exported="false" - android:theme="@style/PipTheme" - android:launchMode="singleTop" - android:taskAffinity="" - android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|locale|layoutDirection" - android:resizeableActivity="true" - android:supportsPictureInPicture="true" - androidprv:alwaysFocusable="true" - android:excludeFromRecents="true" /> - <!-- started from TvNotificationPanel --> <activity android:name=".statusbar.tv.notifications.TvNotificationPanelActivity" diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java index 6c6c9270bc84..3058d9466121 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java @@ -127,6 +127,13 @@ public interface ClockPlugin extends Plugin { default void onTimeZoneChanged(TimeZone timeZone) {} /** + * Notifies that the time format has changed. + * + * @param timeFormat "12" for 12-hour format, "24" for 24-hour format + */ + default void onTimeFormatChanged(String timeFormat) {} + + /** * Indicates whether the keyguard status area (date) should be shown below * the clock. */ diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index c82bda6620bd..19f82480284e 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -73,13 +73,15 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="right" - android:textSize="80dp" + android:textSize="100dp" android:letterSpacing="0.02" android:lineSpacingMultiplier=".8" android:includeFontPadding="false" android:fontFamily="@font/clock" android:typeface="monospace" android:elegantTextHeight="false" + dozeWeight="200" + lockScreenWeight="300" /> </FrameLayout> <FrameLayout @@ -95,13 +97,15 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:gravity="center_horizontal" - android:textSize="180dp" + android:textSize="200dp" android:letterSpacing="0.02" android:lineSpacingMultiplier=".8" android:includeFontPadding="false" android:fontFamily="@font/clock" android:typeface="monospace" android:elegantTextHeight="false" + dozeWeight="100" + lockScreenWeight="400" /> </FrameLayout> <include layout="@layout/keyguard_status_area" diff --git a/packages/SystemUI/res-keyguard/values/attrs.xml b/packages/SystemUI/res-keyguard/values/attrs.xml index 293b683497f8..bfcc56cdc660 100644 --- a/packages/SystemUI/res-keyguard/values/attrs.xml +++ b/packages/SystemUI/res-keyguard/values/attrs.xml @@ -39,4 +39,9 @@ </declare-styleable> <attr name="passwordStyle" format="reference" /> + + <declare-styleable name="AnimatableClockView"> + <attr name="dozeWeight" format="integer" /> + <attr name="lockScreenWeight" format="integer" /> + </declare-styleable> </resources> diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index d95ab374a5cb..4b6621379b44 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -79,8 +79,8 @@ is not fully charged, and it's plugged into a slow charger, say that it's charging slowly. --> <string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string> - <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's limited temporarily. --> - <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Battery limited temporarily</string> + <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's optimizing for battery health. --> + <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Optimizing for battery health</string> <!-- When the lock screen is showing and the battery is low, warn user to plug in the phone soon. --> diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index bc48e8fe3eea..71a1cc292ec9 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -137,5 +137,4 @@ <item name="android:shadowColor">@color/keyguard_shadow_color</item> <item name="android:shadowRadius">?attr/shadowRadius</item> </style> - </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 01b55b70d5ad..880dd378e390 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -357,6 +357,9 @@ the notification is not swiped enough to dismiss it. --> <bool name="config_showNotificationGear">true</bool> + <!-- Whether or not a background should be drawn behind a notification. --> + <bool name="config_drawNotificationBackground">true</bool> + <!-- Whether or the notifications can be shown and dismissed with a drag. --> <bool name="config_enableNotificationShadeDrag">true</bool> diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java index 3157a5a8fc5b..7c6a27446c78 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java @@ -24,6 +24,8 @@ import com.android.settingslib.Utils; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.util.ViewController; +import java.util.TimeZone; + /** * Controls the color of a GradientTextClock. */ @@ -62,12 +64,26 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie } /** - * Updates the time for this view. + * Updates the time for the view. */ public void refreshTime() { mView.refreshTime(); } + /** + * Updates the timezone for the view. + */ + public void onTimeZoneChanged(TimeZone timeZone) { + mView.onTimeZoneChanged(timeZone); + } + + /** + * Trigger a time format update + */ + public void refreshFormat() { + mView.refreshFormat(); + } + private void initColors() { mLockScreenColors[0] = Utils.getColorAttrDefaultColor(getContext(), com.android.systemui.R.attr.wallpaperTextColor); diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java index 6d1d42e2988b..ca99563986b4 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java @@ -19,13 +19,17 @@ package com.android.keyguard; import android.annotation.FloatRange; import android.annotation.IntRange; import android.content.Context; +import android.content.res.TypedArray; import android.graphics.Canvas; import android.icu.text.DateTimePatternGenerator; import android.text.format.DateFormat; import android.util.AttributeSet; import android.widget.TextView; +import com.android.systemui.R; + import java.util.Calendar; +import java.util.TimeZone; import kotlin.Unit; @@ -38,11 +42,14 @@ public class AnimatableClockView extends TextView { private static final CharSequence FORMAT_24_HOUR = "HH\nmm"; private static final long ANIM_DURATION = 300; + private final Calendar mTime = Calendar.getInstance(); + private CharSequence mFormat; private CharSequence mDescFormat; - private Calendar mTime = Calendar.getInstance(); private int[] mDozingColors; private int[] mLockScreenColors; + private final int mDozingWeight; + private final int mLockScreenWeight; private TextAnimator mTextAnimator = null; private Runnable mOnTextAnimatorInitialized; @@ -62,13 +69,21 @@ public class AnimatableClockView extends TextView { public AnimatableClockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - updateTimeFormat(); + TypedArray ta = context.obtainStyledAttributes( + attrs, R.styleable.AnimatableClockView, defStyleAttr, defStyleRes); + try { + mDozingWeight = ta.getInt(R.styleable.AnimatableClockView_dozeWeight, 100); + mLockScreenWeight = ta.getInt(R.styleable.AnimatableClockView_lockScreenWeight, 300); + } finally { + ta.recycle(); + } + refreshFormat(); } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); - updateTimeFormat(); + refreshFormat(); } @Override @@ -82,6 +97,11 @@ public class AnimatableClockView extends TextView { setContentDescription(DateFormat.format(mDescFormat, mTime)); } + void onTimeZoneChanged(TimeZone timeZone) { + mTime.setTimeZone(timeZone); + refreshFormat(); + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -113,7 +133,7 @@ public class AnimatableClockView extends TextView { } void animateDoze(boolean isDozing, boolean animate) { - setTextStyle(isDozing ? 100 : 300 /* weight */, + setTextStyle(isDozing ? mDozingWeight : mLockScreenWeight /* weight */, -1, isDozing ? mDozingColors : mLockScreenColors, animate); @@ -144,10 +164,11 @@ public class AnimatableClockView extends TextView { } } - private void updateTimeFormat() { + void refreshFormat() { final boolean use24HourFormat = DateFormat.is24HourFormat(getContext()); mFormat = use24HourFormat ? FORMAT_24_HOUR : FORMAT_12_HOUR; mDescFormat = getBestDateTimePattern(getContext(), use24HourFormat ? "Hm" : "hm"); + refreshTime(); } private static String getBestDateTimePattern(Context context, String skeleton) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 68405a1fca68..ab7ba8a0f358 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -156,13 +156,17 @@ public class KeyguardClockSwitch extends RelativeLayout { statusAreaLP.removeRule(RelativeLayout.BELOW); statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START); + statusAreaLP.addRule(RelativeLayout.START_OF, R.id.new_lockscreen_clock_view); + statusAreaLP.width = 0; } else { setPaddingRelative(0, 0, 0, 0); mSmallClockFrame.setVisibility(VISIBLE); mNewLockscreenClockFrame.setVisibility(GONE); statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START); + statusAreaLP.removeRule(RelativeLayout.START_OF); statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view); + statusAreaLP.width = ViewGroup.LayoutParams.WRAP_CONTENT; } requestLayout(); @@ -401,6 +405,17 @@ public class KeyguardClockSwitch extends RelativeLayout { } } + /** + * Notifies that the time format has changed. + * + * @param timeFormat "12" for 12-hour format, "24" for 24-hour format + */ + public void onTimeFormatChanged(String timeFormat) { + if (mClockPlugin != null) { + mClockPlugin.onTimeFormatChanged(timeFormat); + } + } + void updateColors(ColorExtractor.GradientColors colors) { mSupportsDarkText = colors.supportsDarkText(); mColorPalette = colors.getColorPalette(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 829ff9771fb4..4d6e8a90e57b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -17,7 +17,9 @@ package com.android.keyguard; import android.app.WallpaperManager; +import android.content.ContentResolver; import android.content.res.Resources; +import android.provider.Settings; import android.text.format.DateFormat; import android.util.TypedValue; import android.view.View; @@ -90,6 +92,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS }; private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin; + private String mTimeFormat; @Inject public KeyguardClockSwitchController( @@ -98,7 +101,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS StatusBarStateController statusBarStateController, SysuiColorExtractor colorExtractor, ClockManager clockManager, KeyguardSliceViewController keyguardSliceViewController, - NotificationIconAreaController notificationIconAreaController) { + NotificationIconAreaController notificationIconAreaController, + ContentResolver contentResolver) { super(keyguardClockSwitch); mResources = resources; mStatusBarStateController = statusBarStateController; @@ -106,6 +110,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mClockManager = clockManager; mKeyguardSliceViewController = keyguardSliceViewController; mNotificationIconAreaController = notificationIconAreaController; + mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24); } /** @@ -246,12 +251,26 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS void updateTimeZone(TimeZone timeZone) { mView.onTimeZoneChanged(timeZone); + if (mNewLockScreenClockViewController != null) { + mNewLockScreenClockViewController.onTimeZoneChanged(timeZone); + mNewLockScreenLargeClockViewController.onTimeZoneChanged(timeZone); + } } - void refreshFormat() { + void refreshFormat(String timeFormat) { + mTimeFormat = timeFormat; Patterns.update(mResources); mView.setFormat12Hour(Patterns.sClockView12); mView.setFormat24Hour(Patterns.sClockView24); + mView.onTimeFormatChanged(mTimeFormat); + if (mNewLockScreenClockViewController != null) { + mNewLockScreenClockViewController.refreshFormat(); + mNewLockScreenLargeClockViewController.refreshFormat(); + } + } + + void refreshFormat() { + refreshFormat(mTimeFormat); } float getClockTextTopPadding() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index bc81a198c7e6..826020c71159 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -329,6 +329,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } @Override + public void onTimeFormatChanged(String timeFormat) { + mKeyguardClockSwitchController.refreshFormat(timeFormat); + } + + @Override public void onKeyguardVisibilityChanged(boolean showing) { if (showing) { if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index a7e51951e9cd..2071cfaf2bd9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -182,6 +182,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final int MSG_USER_REMOVED = 341; private static final int MSG_KEYGUARD_GOING_AWAY = 342; private static final int MSG_LOCK_SCREEN_MODE = 343; + private static final int MSG_TIME_FORMAT_UPDATE = 344; public static final int LOCK_SCREEN_MODE_NORMAL = 0; public static final int LOCK_SCREEN_MODE_LAYOUT_1 = 1; @@ -280,6 +281,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mCallbacks = Lists.newArrayList(); private ContentObserver mDeviceProvisionedObserver; private ContentObserver mLockScreenModeObserver; + private ContentObserver mTimeFormatChangeObserver; private boolean mSwitchingUser; @@ -1721,6 +1723,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab case MSG_LOCK_SCREEN_MODE: handleLockScreenMode(); break; + case MSG_TIME_FORMAT_UPDATE: + handleTimeFormatUpdate((String) msg.obj); + break; default: super.handleMessage(msg); break; @@ -1866,12 +1871,25 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.SHOW_NEW_LOCKSCREEN), false, mLockScreenModeObserver); + + mTimeFormatChangeObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + mHandler.sendMessage(mHandler.obtainMessage( + MSG_TIME_FORMAT_UPDATE, + Settings.System.getString( + mContext.getContentResolver(), + Settings.System.TIME_12_24))); + } + }; + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.TIME_12_24), + false, mTimeFormatChangeObserver, UserHandle.USER_ALL); } private void updateLockScreenMode() { final int newMode = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.SHOW_NEW_LOCKSCREEN, - isUdfpsEnrolled() ? 1 : 0); + Settings.Global.SHOW_NEW_LOCKSCREEN, LOCK_SCREEN_MODE_LAYOUT_1); if (newMode != mLockScreenMode) { mLockScreenMode = newMode; mHandler.sendEmptyMessage(MSG_LOCK_SCREEN_MODE); @@ -2451,6 +2469,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** + * Handle (@line #MSG_TIME_FORMAT_UPDATE} + * + * @param timeFormat "12" for 12-hour format, "24" for 24-hour format + */ + private void handleTimeFormatUpdate(String timeFormat) { + Assert.isMainThread(); + if (DEBUG) Log.d(TAG, "handleTimeFormatUpdate timeFormat=" + timeFormat); + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onTimeFormatChanged(timeFormat); + } + } + } + + /** * Handle {@link #MSG_BATTERY_UPDATE} */ private void handleBatteryUpdate(BatteryStatus status) { @@ -2687,6 +2721,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return true; } + // change in battery overheat + if (current.health != old.health) { + return true; + } + return false; } @@ -3055,6 +3094,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mContext.getContentResolver().unregisterContentObserver(mLockScreenModeObserver); } + if (mTimeFormatChangeObserver != null) { + mContext.getContentResolver().unregisterContentObserver(mTimeFormatChangeObserver); + } + try { ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver); } catch (RemoteException e) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index b722deab528a..36617c239352 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -58,6 +58,11 @@ public class KeyguardUpdateMonitorCallback { public void onTimeZoneChanged(TimeZone timeZone) { } /** + * Called when time format changes. + */ + public void onTimeFormatChanged(String timeFormat) { } + + /** * Called when the carrier PLMN or SPN changes. */ public void onRefreshCarrierInfo() { } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 7127f26a7ed2..275bfaa1023c 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -30,7 +30,6 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.ServiceManager; import android.os.UserHandle; -import android.util.DisplayMetrics; import android.view.Choreographer; import android.view.IWindowManager; import android.view.LayoutInflater; @@ -141,16 +140,6 @@ public class DependencyProvider { return networkController.getDataSaverController(); } - /** */ - @Provides - @SysUISingleton - public DisplayMetrics provideDisplayMetrics(Context context, WindowManager windowManager) { - DisplayMetrics displayMetrics = new DisplayMetrics(); - context.getDisplay().getMetrics(displayMetrics); - return displayMetrics; - } - - /** */ @Provides @SysUISingleton public INotificationManager provideINotificationManager() { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java index d683a74aedcc..a89c7acea984 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java @@ -24,7 +24,6 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.dagger.qualifiers.TestHarness; import com.android.systemui.util.concurrency.GlobalConcurrencyModule; -import com.android.wm.shell.animation.FlingAnimationUtils; import javax.inject.Singleton; @@ -51,15 +50,12 @@ import dagger.Provides; GlobalConcurrencyModule.class}) public class GlobalModule { - // TODO(b/162923491): This should not be a singleton at all, the display metrics can change and - // callers should be creating a new builder on demand - @Singleton + /** */ @Provides - static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder( - Context context) { + public DisplayMetrics provideDisplayMetrics(Context context) { DisplayMetrics displayMetrics = new DisplayMetrics(); context.getDisplay().getMetrics(displayMetrics); - return new FlingAnimationUtils.Builder(displayMetrics); + return displayMetrics; } /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */ diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java new file mode 100644 index 000000000000..6050c2b90e34 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java @@ -0,0 +1,64 @@ +/* + * 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.screenshot; + +@SuppressWarnings("PointlessBooleanExpression") +class LogConfig { + + /** Log ALL the things... */ + private static final boolean DEBUG_ALL = false; + + /** Default log logTag for screenshot code */ + private static final String TAG_SS = "Screenshot"; + + /** Use class name as Log tag instead of the default */ + private static final boolean TAG_WITH_CLASS_NAME = false; + + /** Action creation and user selection: Share, Save, Edit, Delete, Smart action, etc */ + static final boolean DEBUG_ACTIONS = DEBUG_ALL || false; + + /** Debug info about animations such as start, complete and cancel */ + static final boolean DEBUG_ANIM = DEBUG_ALL || false; + + /** Whenever Uri is supplied to consumer, or onComplete runnable is run() */ + static final boolean DEBUG_CALLBACK = DEBUG_ALL || false; + + /** Logs information about dismissing the screenshot tool */ + static final boolean DEBUG_DISMISS = DEBUG_ALL || false; + + /** Touch or key event driven action or side effects */ + static final boolean DEBUG_INPUT = DEBUG_ALL || false; + + /** Scroll capture usage */ + static final boolean DEBUG_SCROLL = DEBUG_ALL || false; + + /** Service lifecycle events and callbacks */ + static final boolean DEBUG_SERVICE = DEBUG_ALL || false; + + /** Storage related actions, Bitmap.compress, ContentManager, etc */ + static final boolean DEBUG_STORAGE = DEBUG_ALL || false; + + /** High level logical UI actions: timeout, onConfigChanged, insets, show actions, reset */ + static final boolean DEBUG_UI = DEBUG_ALL || false; + + /** Interactions with Window and WindowManager */ + static final boolean DEBUG_WINDOW = DEBUG_ALL || false; + + static String logTag(Class<?> cls) { + return TAG_WITH_CLASS_NAME ? cls.getSimpleName() : TAG_SS; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index b2ebf3f700b9..f4ce77acb8ec 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -16,6 +16,11 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; +import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; +import static com.android.systemui.screenshot.LogConfig.DEBUG_STORAGE; +import static com.android.systemui.screenshot.LogConfig.logTag; + import android.app.ActivityTaskManager; import android.app.Notification; import android.app.PendingIntent; @@ -45,7 +50,7 @@ import android.provider.MediaStore; import android.provider.MediaStore.MediaColumns; import android.text.TextUtils; import android.text.format.DateUtils; -import android.util.Slog; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; @@ -73,8 +78,8 @@ import java.util.concurrent.CompletableFuture; /** * An AsyncTask that saves an image to the media store in the background. */ -class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { - private static final String TAG = "SaveImageInBackgroundTask"; +class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { + private static final String TAG = logTag(SaveImageInBackgroundTask.class); private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png"; private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s"; @@ -121,6 +126,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... paramsUnused) { if (isCancelled()) { + if (DEBUG_STORAGE) { + Log.d(TAG, "cancelled! returning null"); + } return null; } Thread.currentThread().setPriority(Thread.MAX_PRIORITY); @@ -151,9 +159,19 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { try { // First, write the actual data for our screenshot try (OutputStream out = resolver.openOutputStream(uri)) { + if (DEBUG_STORAGE) { + Log.d(TAG, "Compressing PNG:" + + " w=" + image.getWidth() + " h=" + image.getHeight()); + } if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) { + if (DEBUG_STORAGE) { + Log.d(TAG, "Bitmap.compress returned false"); + } throw new IOException("Failed to compress"); } + if (DEBUG_STORAGE) { + Log.d(TAG, "Done compressing PNG"); + } } // Next, write metadata to help index the screenshot @@ -181,7 +199,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, DateTimeFormatter.ofPattern("XXX").format(time)); } - + if (DEBUG_STORAGE) { + Log.d(TAG, "Writing EXIF metadata"); + } exif.saveAttributes(); } @@ -190,6 +210,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { values.put(MediaColumns.IS_PENDING, 0); values.putNull(MediaColumns.DATE_EXPIRES); resolver.update(uri, values, null, null); + if (DEBUG_STORAGE) { + Log.d(TAG, "Completed writing to ContentManager"); + } } catch (Exception e) { resolver.delete(uri, null); throw e; @@ -215,15 +238,24 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri); mParams.mActionsReadyListener.onActionsReady(mImageData); + if (DEBUG_CALLBACK) { + Log.d(TAG, "finished background processing, Calling (Consumer<Uri>) " + + "finisher.accept(\"" + mImageData.uri + "\""); + } mParams.finisher.accept(mImageData.uri); mParams.image = null; } catch (Exception e) { // IOException/UnsupportedOperationException may be thrown if external storage is // not mounted - Slog.e(TAG, "unable to save screenshot", e); + if (DEBUG_STORAGE) { + Log.d(TAG, "Failed to store screenshot", e); + } mParams.clearImage(); mImageData.reset(); mParams.mActionsReadyListener.onActionsReady(mImageData); + if (DEBUG_CALLBACK) { + Log.d(TAG, "Calling (Consumer<Uri>) finisher.accept(null)"); + } mParams.finisher.accept(null); } @@ -245,6 +277,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { // params from the ctor in any case. mImageData.reset(); mParams.mActionsReadyListener.onActionsReady(mImageData); + if (DEBUG_CALLBACK) { + Log.d(TAG, "onCancelled, calling (Consumer<Uri>) finisher.accept(null)"); + } mParams.finisher.accept(null); mParams.clearImage(); } @@ -380,7 +415,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { try { return ActivityTaskManager.getService().getLastResumedActivityUserId(); } catch (RemoteException e) { - Slog.w(TAG, "getUserHandleOfForegroundApplication: ", e); + if (DEBUG_ACTIONS) { + Log.d(TAG, "Failed to get UserHandle of foreground app: ", e); + } return context.getUserId(); } } @@ -421,6 +458,4 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { .putExtra(ScreenshotController.EXTRA_ID, screenshotId) .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled); } - - } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index d7f9c6163b1d..6a4e93be0fe5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -21,6 +21,14 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM; +import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; +import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; +import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT; +import static com.android.systemui.screenshot.LogConfig.DEBUG_UI; +import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW; +import static com.android.systemui.screenshot.LogConfig.logTag; + import static java.util.Objects.requireNonNull; import android.animation.Animator; @@ -75,6 +83,7 @@ import javax.inject.Inject; * Controls the state and flow for screenshots. */ public class ScreenshotController { + private static final String TAG = logTag(ScreenshotController.class); /** * POD used in the AsyncTask which saves an image in the background. */ @@ -110,12 +119,10 @@ public class ScreenshotController { } } - abstract static class ActionsReadyListener { - abstract void onActionsReady(ScreenshotController.SavedImageData imageData); + interface ActionsReadyListener { + void onActionsReady(ScreenshotController.SavedImageData imageData); } - private static final String TAG = "ScreenshotController"; - // These strings are used for communicating the action invoked to // ScreenshotNotificationSmartActionsProvider. static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type"; @@ -166,6 +173,9 @@ public class ScreenshotController { public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_CORNER_TIMEOUT: + if (DEBUG_UI) { + Log.d(TAG, "Corner timeout hit"); + } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT); ScreenshotController.this.dismissScreenshot(false); break; @@ -292,13 +302,17 @@ public class ScreenshotController { * Clears current screenshot */ void dismissScreenshot(boolean immediate) { + if (DEBUG_DISMISS) { + Log.d(TAG, "dismissScreenshot(immediate=" + immediate + ")"); + } // If we're already animating out, don't restart the animation // (but do obey an immediate dismissal) if (!immediate && mScreenshotView.isDismissing()) { - Log.v(TAG, "Already dismissing, ignoring duplicate command"); + if (DEBUG_DISMISS) { + Log.v(TAG, "Already dismissing, ignoring duplicate command"); + } return; } - Log.v(TAG, "Clearing screenshot"); mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT); if (immediate) { resetScreenshotView(); @@ -308,12 +322,17 @@ public class ScreenshotController { } /** - * Update assets (called when the dark theme status changes). We only need to update the dismiss - * button and the actions container background, since the buttons are re-inflated on demand. + * Update resources on configuration change. Reinflate for theme/color changes. */ private void reloadAssets() { + if (DEBUG_UI) { + Log.d(TAG, "reloadAssets()"); + } boolean wasAttached = mDecorView.isAttachedToWindow(); if (wasAttached) { + if (DEBUG_WINDOW) { + Log.d(TAG, "Removing screenshot window"); + } mWindowManager.removeView(mDecorView); } @@ -336,6 +355,9 @@ public class ScreenshotController { // TODO(159460485): Remove this when focus is handled properly in the system mScreenshotView.setOnTouchListener((v, event) -> { if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) { + if (DEBUG_INPUT) { + Log.d(TAG, "onTouch: ACTION_OUTSIDE"); + } // Once the user touches outside, stop listening for input setWindowFocusable(false); } @@ -344,6 +366,9 @@ public class ScreenshotController { mScreenshotView.setOnKeyListener((v, keyCode, event) -> { if (keyCode == KeyEvent.KEYCODE_BACK) { + if (DEBUG_INPUT) { + Log.d(TAG, "onKeyEvent: KeyEvent.KEYCODE_BACK"); + } dismissScreenshot(false); return true; } @@ -373,10 +398,16 @@ public class ScreenshotController { Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap(); if (screenshot == null) { - Log.e(TAG, "Screenshot bitmap was null"); + Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null"); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); + if (DEBUG_CALLBACK) { + Log.d(TAG, "Supplying null to Consumer<Uri>"); + } finisher.accept(null); + if (DEBUG_CALLBACK) { + Log.d(TAG, "Calling mOnCompleteRunnable.run()"); + } mOnCompleteRunnable.run(); return; } @@ -399,12 +430,17 @@ public class ScreenshotController { if (!mScreenshotView.isDismissing()) { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED); } + if (DEBUG_WINDOW) { + Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. " + + "(dismissing=" + mScreenshotView.isDismissing() + ")"); + } mScreenshotView.reset(); } mScreenBitmap = screenshot; if (!isUserSetupComplete()) { + Log.w(TAG, "User setup not complete, displaying toast only"); // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing // and sharing shouldn't be exposed to the user. saveScreenshotAndToast(finisher); @@ -416,6 +452,9 @@ public class ScreenshotController { mScreenBitmap.prepareToDraw(); if (mConfigChanges.applyNewConfig(mContext.getResources())) { + if (DEBUG_UI) { + Log.d(TAG, "saveScreenshot: reloading assets"); + } reloadAssets(); } @@ -450,25 +489,21 @@ public class ScreenshotController { mCameraSound.play(MediaActionSound.SHUTTER_CLICK); }); - saveScreenshotInWorkerThread(finisher, - new ScreenshotController.ActionsReadyListener() { - @Override - void onActionsReady(ScreenshotController.SavedImageData imageData) { - finisher.accept(imageData.uri); - if (imageData.uri == null) { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED); - mNotificationsController.notifyScreenshotError( - R.string.screenshot_failed_to_save_text); - } else { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED); - - mScreenshotHandler.post(() -> { - Toast.makeText(mContext, R.string.screenshot_saved_title, - Toast.LENGTH_SHORT).show(); - }); - } - } - }); + saveScreenshotInWorkerThread(finisher, imageData -> { + if (DEBUG_CALLBACK) { + Log.d(TAG, "returning URI to finisher (Consumer<URI>): " + imageData.uri); + } + finisher.accept(imageData.uri); + if (imageData.uri == null) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED); + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_save_text); + } else { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED); + mScreenshotHandler.post(() -> Toast.makeText(mContext, + R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show()); + } + }); } /** @@ -479,37 +514,46 @@ public class ScreenshotController { mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT); mScreenshotHandler.post(() -> { if (!mScreenshotView.isAttachedToWindow()) { + if (DEBUG_WINDOW) { + Log.d(TAG, "Adding screenshot window"); + } mWindowManager.addView(mWindow.getDecorView(), mWindowLayoutParams); } mScreenshotView.prepareForAnimation(mScreenBitmap, screenInsets); mScreenshotHandler.post(() -> { + if (DEBUG_WINDOW) { + Log.d(TAG, "adding OnComputeInternalInsetsListener"); + } mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener( mScreenshotView); mScreenshotAnimation = mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash); - saveScreenshotInWorkerThread(finisher, - new ScreenshotController.ActionsReadyListener() { - @Override - void onActionsReady( - ScreenshotController.SavedImageData imageData) { - showUiOnActionsReady(imageData); - } - }); + saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady); // Play the shutter sound to notify that we've taken a screenshot mCameraSound.play(MediaActionSound.SHUTTER_CLICK); + if (DEBUG_ANIM) { + Log.d(TAG, "starting post-screenshot animation"); + } mScreenshotAnimation.start(); }); }); } + /** Reset screenshot view and then call onCompleteRunnable */ private void resetScreenshotView() { + if (DEBUG_UI) { + Log.d(TAG, "resetScreenshotView"); + } if (mScreenshotView.isAttachedToWindow()) { + if (DEBUG_WINDOW) { + Log.d(TAG, "Removing screenshot window"); + } mWindowManager.removeView(mDecorView); } mScreenshotView.reset(); @@ -519,8 +563,7 @@ public class ScreenshotController { /** * Creates a new worker thread and saves the screenshot to the media store. */ - private void saveScreenshotInWorkerThread( - Consumer<Uri> finisher, + private void saveScreenshotInWorkerThread(Consumer<Uri> finisher, @Nullable ScreenshotController.ActionsReadyListener actionsReadyListener) { ScreenshotController.SaveImageInBackgroundData data = new ScreenshotController.SaveImageInBackgroundData(); @@ -530,13 +573,7 @@ public class ScreenshotController { if (mSaveInBgTask != null) { // just log success/failure for the pre-existing screenshot - mSaveInBgTask.setActionsReadyListener( - new ScreenshotController.ActionsReadyListener() { - @Override - void onActionsReady(ScreenshotController.SavedImageData imageData) { - logSuccessOnActionsReady(imageData); - } - }); + mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady); } mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data); @@ -555,6 +592,9 @@ public class ScreenshotController { SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS, AccessibilityManager.FLAG_CONTENT_CONTROLS); + if (DEBUG_UI) { + Log.d(TAG, "Showing UI actions, dismiss timeout: " + timeoutMs + " ms"); + } mScreenshotHandler.sendMessageDelayed( mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT), timeoutMs); @@ -600,6 +640,9 @@ public class ScreenshotController { * shown. */ private void setWindowFocusable(boolean focusable) { + if (DEBUG_WINDOW) { + Log.d(TAG, "setWindowFocusable: " + focusable); + } if (focusable) { mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; } else { @@ -618,9 +661,10 @@ public class ScreenshotController { if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) { - Log.e(TAG, String.format( - "Provided bitmap and insets create degenerate region: %dx%d %s", - bitmap.getWidth(), bitmap.getHeight(), bitmapInsets)); + if (DEBUG_UI) { + Log.e(TAG, "Provided bitmap and insets create degenerate region: " + + bitmap.getWidth() + "x" + bitmap.getHeight() + " " + bitmapInsets); + } return false; } @@ -628,11 +672,10 @@ public class ScreenshotController { float boundsAspect = ((float) screenBounds.width()) / screenBounds.height(); boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f; - if (!matchWithinTolerance) { - Log.d(TAG, String.format("aspectRatiosMatch: don't match bitmap: %f, bounds: %f", - insettedBitmapAspect, boundsAspect)); + if (DEBUG_UI) { + Log.d(TAG, "aspectRatiosMatch: don't match bitmap: " + insettedBitmapAspect + + ", bounds: " + boundsAspect); } - return matchWithinTolerance; } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java index 63f323ed2768..29f67f348f7b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java @@ -16,6 +16,9 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; +import static com.android.systemui.screenshot.LogConfig.logTag; + import android.app.Notification; import android.content.ComponentName; import android.graphics.Bitmap; @@ -32,6 +35,9 @@ import java.util.concurrent.CompletableFuture; * in order to provide smart actions in the screenshot notification. */ public class ScreenshotNotificationSmartActionsProvider { + + private static final String TAG = logTag(ScreenshotNotificationSmartActionsProvider.class); + /* Key provided in the notification action to get the type of smart action. */ public static final String ACTION_TYPE = "action_type"; public static final String DEFAULT_ACTION_TYPE = "Smart Action"; @@ -51,29 +57,21 @@ public class ScreenshotNotificationSmartActionsProvider { ERROR, TIMEOUT } - - private static final String TAG = "ScreenshotActions"; - /** * Default implementation that returns an empty list. * This method is overridden in vendor-specific Sys UI implementation. * - * @param screenshotId A generated random unique id for the screenshot. - * @param screenshotFileName name of the file where the screenshot will be written. - * @param bitmap The bitmap of the screenshot. The bitmap config must be {@link - * HARDWARE}. - * @param componentName Contains package and activity class names where the screenshot was - * taken. This is used as an additional signal to generate and rank - * more relevant actions. - * @param userHandle The user handle of the app where the screenshot was taken. + * @param screenshotId a unique id for the screenshot + * @param screenshotUri uri where the screenshot has been stored + * @param bitmap the screenshot, config must be {@link Bitmap.Config#HARDWARE} + * @param componentName name of the foreground component when the screenshot was taken + * @param userHandle user handle of the foreground task owner */ - public CompletableFuture<List<Notification.Action>> getActions( - String screenshotId, - Uri screenshotUri, - Bitmap bitmap, - ComponentName componentName, - UserHandle userHandle) { - Log.d(TAG, "Returning empty smart action list."); + public CompletableFuture<List<Notification.Action>> getActions(String screenshotId, + Uri screenshotUri, Bitmap bitmap, ComponentName componentName, UserHandle userHandle) { + if (DEBUG_ACTIONS) { + Log.d(TAG, "Returning empty smart action list."); + } return CompletableFuture.completedFuture(Collections.emptyList()); } @@ -88,7 +86,9 @@ public class ScreenshotNotificationSmartActionsProvider { */ public void notifyOp(String screenshotId, ScreenshotOp op, ScreenshotOpStatus status, long durationMs) { - Log.d(TAG, "Return without notify."); + if (DEBUG_ACTIONS) { + Log.d(TAG, "SmartActions: notifyOp() - return without notify"); + } } /** @@ -100,6 +100,8 @@ public class ScreenshotNotificationSmartActionsProvider { * @param isSmartAction whether action invoked was a smart action. */ public void notifyAction(String screenshotId, String action, boolean isSmartAction) { - Log.d(TAG, "Return without notify."); + if (DEBUG_ACTIONS) { + Log.d(TAG, "SmartActions: notifyAction: return without notify"); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java index 468602a5369e..1184dc7fe1a4 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java @@ -18,6 +18,9 @@ package com.android.systemui.screenshot; import static android.os.AsyncTask.THREAD_POOL_EXECUTOR; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; +import static com.android.systemui.screenshot.LogConfig.logTag; + import android.app.ActivityManager; import android.app.Notification; import android.content.ComponentName; @@ -27,7 +30,7 @@ import android.net.Uri; import android.os.Handler; import android.os.SystemClock; import android.os.UserHandle; -import android.util.Slog; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.SystemUIFactory; @@ -47,7 +50,7 @@ import javax.inject.Inject; */ @SysUISingleton public class ScreenshotSmartActions { - private static final String TAG = "ScreenshotSmartActions"; + private static final String TAG = logTag(ScreenshotSmartActions.class); @Inject public ScreenshotSmartActions() {} @@ -57,18 +60,24 @@ public class ScreenshotSmartActions { String screenshotId, Uri screenshotUri, Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider, boolean smartActionsEnabled, UserHandle userHandle) { + if (DEBUG_ACTIONS) { + Log.d(TAG, String.format("getSmartActionsFuture id=%s, uri=%s, provider=%s, " + + "smartActionsEnabled=%b, userHandle=%s", screenshotId, screenshotUri, + smartActionsProvider.getClass(), smartActionsEnabled, userHandle)); + } if (!smartActionsEnabled) { - Slog.i(TAG, "Screenshot Intelligence not enabled, returning empty list."); + if (DEBUG_ACTIONS) { + Log.d(TAG, "Screenshot Intelligence not enabled, returning empty list."); + } return CompletableFuture.completedFuture(Collections.emptyList()); } if (image.getConfig() != Bitmap.Config.HARDWARE) { - Slog.w(TAG, String.format( - "Bitmap expected: Hardware, Bitmap found: %s. Returning empty list.", - image.getConfig())); + if (DEBUG_ACTIONS) { + Log.d(TAG, String.format("Bitmap expected: Hardware, Bitmap found: %s. " + + "Returning empty list.", image.getConfig())); + } return CompletableFuture.completedFuture(Collections.emptyList()); } - - Slog.d(TAG, "Screenshot from user profile: " + userHandle.getIdentifier()); CompletableFuture<List<Notification.Action>> smartActionsFuture; long startTimeMs = SystemClock.uptimeMillis(); try { @@ -83,7 +92,9 @@ public class ScreenshotSmartActions { } catch (Throwable e) { long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList()); - Slog.e(TAG, "Failed to get future for screenshot notification smart actions.", e); + if (DEBUG_ACTIONS) { + Log.e(TAG, "Failed to get future for screenshot notification smart actions.", e); + } notifyScreenshotOp(screenshotId, smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS, ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR, @@ -97,12 +108,18 @@ public class ScreenshotSmartActions { CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs, ScreenshotNotificationSmartActionsProvider smartActionsProvider) { long startTimeMs = SystemClock.uptimeMillis(); + if (DEBUG_ACTIONS) { + Log.d(TAG, String.format("getSmartActions id=%s, timeoutMs=%d, provider=%s", + screenshotId, timeoutMs, smartActionsProvider.getClass())); + } try { List<Notification.Action> actions = smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS); long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; - Slog.d(TAG, String.format("Got %d smart actions. Wait time: %d ms", - actions.size(), waitTimeMs)); + if (DEBUG_ACTIONS) { + Log.d(TAG, String.format("Got %d smart actions. Wait time: %d ms", + actions.size(), waitTimeMs)); + } notifyScreenshotOp(screenshotId, smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS, ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS, @@ -110,8 +127,10 @@ public class ScreenshotSmartActions { return actions; } catch (Throwable e) { long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; - Slog.e(TAG, String.format("Error getting smart actions. Wait time: %d ms", waitTimeMs), - e); + if (DEBUG_ACTIONS) { + Log.e(TAG, String.format("Error getting smart actions. Wait time: %d ms", + waitTimeMs), e); + } ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status = (e instanceof TimeoutException) ? ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT @@ -127,10 +146,14 @@ public class ScreenshotSmartActions { ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp op, ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) { + if (DEBUG_ACTIONS) { + Log.d(TAG, String.format("%s notifyOp: %s id=%s, status=%s, durationMs=%d", + smartActionsProvider.getClass(), op, screenshotId, status, durationMs)); + } try { smartActionsProvider.notifyOp(screenshotId, op, status, durationMs); } catch (Throwable e) { - Slog.e(TAG, "Error in notifyScreenshotOp: ", e); + Log.e(TAG, "Error in notifyScreenshotOp: ", e); } } @@ -140,9 +163,13 @@ public class ScreenshotSmartActions { ScreenshotNotificationSmartActionsProvider provider = SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider( context, THREAD_POOL_EXECUTOR, new Handler()); + if (DEBUG_ACTIONS) { + Log.e(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b", + provider.getClass(), action, screenshotId, isSmartAction)); + } provider.notifyAction(screenshotId, action, isSmartAction); } catch (Throwable e) { - Slog.e(TAG, "Error in notifyScreenshotAction: ", e); + Log.e(TAG, "Error in notifyScreenshotAction: ", e); } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 3814bd2999e9..cce60f9c7c9d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -18,6 +18,14 @@ package com.android.systemui.screenshot; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM; +import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; +import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT; +import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL; +import static com.android.systemui.screenshot.LogConfig.DEBUG_UI; +import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW; +import static com.android.systemui.screenshot.LogConfig.logTag; + import static java.util.Objects.requireNonNull; import android.animation.Animator; @@ -75,7 +83,7 @@ import java.util.function.Consumer; public class ScreenshotView extends FrameLayout implements ViewTreeObserver.OnComputeInternalInsetsListener { - private static final String TAG = "ScreenshotView"; + private static final String TAG = logTag(ScreenshotView.class); private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133; private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217; @@ -166,12 +174,18 @@ public class ScreenshotView extends FrameLayout implements * @param onClick the action to take when the chip is clicked. */ public void showScrollChip(Runnable onClick) { + if (DEBUG_SCROLL) { + Log.d(TAG, "Showing Scroll option"); + } mScrollChip.setVisibility(VISIBLE); - mScrollChip.setOnClickListener((v) -> - onClick.run() - // TODO Logging, store event consumer to a field - //onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED); - ); + mScrollChip.setOnClickListener((v) -> { + if (DEBUG_INPUT) { + Log.d(TAG, "scroll chip tapped"); + } + onClick.run(); + // TODO Logging, store event consumer to a field + //onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED); + }); } @Override // ViewTreeObserver.OnComputeInternalInsetsListener @@ -179,15 +193,13 @@ public class ScreenshotView extends FrameLayout implements inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); Region touchRegion = new Region(); - Rect screenshotRect = new Rect(); - mScreenshotPreview.getBoundsOnScreen(screenshotRect); - touchRegion.op(screenshotRect, Region.Op.UNION); - Rect actionsRect = new Rect(); - mActionsContainer.getBoundsOnScreen(actionsRect); - touchRegion.op(actionsRect, Region.Op.UNION); - Rect dismissRect = new Rect(); - mDismissButton.getBoundsOnScreen(dismissRect); - touchRegion.op(dismissRect, Region.Op.UNION); + final Rect tmpRect = new Rect(); + mScreenshotPreview.getBoundsOnScreen(tmpRect); + touchRegion.op(tmpRect, Region.Op.UNION); + mActionsContainer.getBoundsOnScreen(tmpRect); + touchRegion.op(tmpRect, Region.Op.UNION); + mDismissButton.getBoundsOnScreen(tmpRect); + touchRegion.op(tmpRect, Region.Op.UNION); if (QuickStepContract.isGesturalMode(mNavMode)) { // Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE @@ -362,7 +374,6 @@ public class ScreenshotView extends FrameLayout implements toCorner.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); mScreenshotPreview.setVisibility(View.VISIBLE); } }); @@ -380,8 +391,13 @@ public class ScreenshotView extends FrameLayout implements dropInAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); + if (DEBUG_ANIM) { + Log.d(TAG, "drop-in animation completed"); + } mDismissButton.setOnClickListener(view -> { + if (DEBUG_INPUT) { + Log.d(TAG, "dismiss button clicked"); + } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL); animateDismissal(); }); @@ -497,7 +513,7 @@ public class ScreenshotView extends FrameLayout implements try { imageData.editAction.actionIntent.send(); } catch (PendingIntent.CanceledException e) { - Log.e(TAG, "Intent cancelled", e); + Log.e(TAG, "PendingIntent was cancelled", e); } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED); animateDismissal(); @@ -543,6 +559,9 @@ public class ScreenshotView extends FrameLayout implements } private void animateDismissal(Animator dismissAnimation) { + if (DEBUG_WINDOW) { + Log.d(TAG, "removing OnComputeInternalInsetsListener"); + } getViewTreeObserver().removeOnComputeInternalInsetsListener(this); mDismissAnimation = dismissAnimation; mDismissAnimation.addListener(new AnimatorListenerAdapter() { @@ -551,6 +570,9 @@ public class ScreenshotView extends FrameLayout implements @Override public void onAnimationCancel(Animator animation) { super.onAnimationCancel(animation); + if (DEBUG_ANIM) { + Log.d(TAG, "Cancelled dismiss animation"); + } mCancelled = true; } @@ -558,17 +580,33 @@ public class ScreenshotView extends FrameLayout implements public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (!mCancelled) { + if (DEBUG_ANIM) { + Log.d(TAG, "after dismiss animation, calling onDismissRunnable.run()"); + } mOnDismissRunnable.run(); } } }); + if (DEBUG_ANIM) { + Log.d(TAG, "Starting dismiss animation"); + } mDismissAnimation.start(); } void reset() { + if (DEBUG_UI) { + Log.d(TAG, "reset screenshot view"); + } + if (mDismissAnimation != null && mDismissAnimation.isRunning()) { + if (DEBUG_ANIM) { + Log.d(TAG, "cancelling dismiss animation"); + } mDismissAnimation.cancel(); } + if (DEBUG_WINDOW) { + Log.d(TAG, "removing OnComputeInternalInsetsListener"); + } // Make sure we clean up the view tree observer getViewTreeObserver().removeOnComputeInternalInsetsListener(this); // Clear any references to the bitmap @@ -637,10 +675,9 @@ public class ScreenshotView extends FrameLayout implements BitmapDrawable bitmapDrawable = new BitmapDrawable(res, bitmap); if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) { - Log.e(TAG, String.format( - "Can't create insetted drawable, using 0 insets " - + "bitmap and insets create degenerate region: %dx%d %s", - bitmap.getWidth(), bitmap.getHeight(), insets)); + Log.e(TAG, "Can't create inset drawable, using 0 insets bitmap and insets create " + + "degenerate region: " + bitmap.getWidth() + "x" + bitmap.getHeight() + " " + + bitmapDrawable); return bitmapDrawable; } @@ -689,12 +726,18 @@ public class ScreenshotView extends FrameLayout implements } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { if (isPastDismissThreshold() && (mDismissAnimation == null || !mDismissAnimation.isRunning())) { + if (DEBUG_INPUT) { + Log.d(TAG, "dismiss triggered via swipe gesture"); + } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED); animateDismissal(createSwipeDismissAnimation()); return true; } else if (MathUtils.dist(mStartX, mStartY, event.getRawX(), event.getRawY()) > dpToPx(CLICK_MOVEMENT_THRESHOLD_DP)) { // if we've moved a non-negligible distance (but not past the threshold), + if (DEBUG_DISMISS) { + Log.d(TAG, "swipe gesture abandoned"); + } // start the return animation if ((mDismissAnimation == null || !mDismissAnimation.isRunning())) { createSwipeReturnAnimation().start(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java index ea835fa94fe8..e159992bc9a5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java @@ -16,6 +16,8 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL; + import static java.util.Objects.requireNonNull; import android.annotation.UiContext; @@ -49,10 +51,7 @@ public class ScrollCaptureClient { @VisibleForTesting static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID; - private static final String TAG = "ScrollCaptureClient"; - - /** Whether to log method names and arguments for most calls */ - private static final boolean DEBUG_TRACE = false; + private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class); /** * A connection to a remote window. Starts a capture session. @@ -155,7 +154,7 @@ public class ScrollCaptureClient { */ public void request(int displayId, int taskId, Consumer<Connection> consumer) { try { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "requestScrollCapture(displayId=" + displayId + ", " + mHostWindowToken + ", taskId=" + taskId + ", consumer=" + consumer + ")"); } @@ -189,7 +188,7 @@ public class ScrollCaptureClient { @Override public void onConnected(IScrollCaptureConnection connection, Rect scrollBounds, Point positionInWindow) throws RemoteException { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "onConnected(connection=" + connection + ", scrollBounds=" + scrollBounds + ", positionInWindow=" + positionInWindow + ")"); } @@ -202,7 +201,7 @@ public class ScrollCaptureClient { @Override public void onUnavailable() throws RemoteException { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "onUnavailable"); } // The targeted app does not support scroll capture @@ -211,7 +210,7 @@ public class ScrollCaptureClient { @Override public void onCaptureStarted() { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "onCaptureStarted()"); } mSessionConsumer.accept(this); @@ -224,7 +223,7 @@ public class ScrollCaptureClient { if (frameNumber != ScrollCaptureViewSupport.NO_FRAME_PRODUCED) { image = mReader.acquireNextImage(); } - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "onCaptureBufferSent(frameNumber=" + frameNumber + ", contentArea=" + contentArea + ") image=" + image); } @@ -237,7 +236,7 @@ public class ScrollCaptureClient { @Override public void onConnectionClosed() { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "onConnectionClosed()"); } disconnect(); @@ -261,7 +260,7 @@ public class ScrollCaptureClient { // -> Error handling: BiConsumer<Session, Throwable> ? @Override public void start(int maxBufferCount, Consumer<Session> sessionConsumer) { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "start(maxBufferCount=" + maxBufferCount + ", sessionConsumer=" + sessionConsumer + ")"); } @@ -288,7 +287,7 @@ public class ScrollCaptureClient { @Override public void end(Runnable listener) { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "end(listener=" + listener + ")"); } if (mStarted) { @@ -319,7 +318,7 @@ public class ScrollCaptureClient { @Override public void requestTile(Rect contentRect, Consumer<CaptureResult> consumer) { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "requestTile(contentRect=" + contentRect + "consumer=" + consumer + ")"); } mRequestRect = new Rect(contentRect); @@ -337,7 +336,7 @@ public class ScrollCaptureClient { */ @Override public void binderDied() { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "binderDied()"); } disconnect(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java index f32529fdaf04..3ad922b57c7c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java @@ -16,6 +16,7 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT; import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE; import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID; @@ -26,7 +27,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; -import android.util.Slog; import javax.inject.Inject; @@ -48,7 +48,9 @@ public class SmartActionsReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT); String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE); - Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent()); + if (DEBUG_ACTIONS) { + Log.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent()); + } ActivityOptions opts = ActivityOptions.makeBasic(); try { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 4aaea8041abd..c2b20d37f3f3 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -20,6 +20,10 @@ import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS; import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE; import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI; +import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; +import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; +import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE; +import static com.android.systemui.screenshot.LogConfig.logTag; import android.app.Service; import android.content.BroadcastReceiver; @@ -53,17 +57,21 @@ import java.util.function.Consumer; import javax.inject.Inject; public class TakeScreenshotService extends Service { - private static final String TAG = "TakeScreenshotService"; + private static final String TAG = logTag(TakeScreenshotService.class); private final ScreenshotController mScreenshot; private final UserManager mUserManager; private final UiEventLogger mUiEventLogger; private final ScreenshotNotificationsController mNotificationsController; private final Handler mHandler; + private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) { + if (DEBUG_DISMISS) { + Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS"); + } mScreenshot.dismissScreenshot(false); } } @@ -73,6 +81,9 @@ public class TakeScreenshotService extends Service { public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager, UiEventLogger uiEventLogger, ScreenshotNotificationsController notificationsController) { + if (DEBUG_SERVICE) { + Log.d(TAG, "new " + this); + } mHandler = new Handler(Looper.getMainLooper(), this::handleMessage); mScreenshot = screenshotController; mUserManager = userManager; @@ -81,13 +92,27 @@ public class TakeScreenshotService extends Service { } @Override + public void onCreate() { + if (DEBUG_SERVICE) { + Log.d(TAG, "onCreate()"); + } + } + + @Override public IBinder onBind(@NonNull Intent intent) { registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS)); - return new Messenger(mHandler).getBinder(); + final Messenger m = new Messenger(mHandler); + if (DEBUG_SERVICE) { + Log.d(TAG, "onBind: returning connection: " + m); + } + return m.getBinder(); } @Override public boolean onUnbind(Intent intent) { + if (DEBUG_SERVICE) { + Log.d(TAG, "onUnbind"); + } if (mScreenshot != null) { mScreenshot.dismissScreenshot(true); } @@ -95,6 +120,14 @@ public class TakeScreenshotService extends Service { return false; } + @Override + public void onDestroy() { + super.onDestroy(); + if (DEBUG_SERVICE) { + Log.d(TAG, "onDestroy"); + } + } + /** Respond to incoming Message via Binder (Messenger) */ private boolean handleMessage(Message msg) { final Messenger replyTo = msg.replyTo; @@ -108,7 +141,13 @@ public class TakeScreenshotService extends Service { Log.w(TAG, "Skipping screenshot because storage is locked!"); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_save_user_locked_text); + if (DEBUG_CALLBACK) { + Log.d(TAG, "handleMessage: calling uriConsumer.accept(null)"); + } uriConsumer.accept(null); + if (DEBUG_CALLBACK) { + Log.d(TAG, "handleMessage: calling onComplete.run()"); + } onComplete.run(); return true; } @@ -120,12 +159,21 @@ public class TakeScreenshotService extends Service { switch (msg.what) { case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: + if (DEBUG_SERVICE) { + Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN"); + } mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete); break; case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION: + if (DEBUG_SERVICE) { + Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION"); + } mScreenshot.takeScreenshotPartial(uriConsumer, onComplete); break; case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE: + if (DEBUG_SERVICE) { + Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE"); + } Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap( screenshotRequest.getBitmapBundle()); Rect screenBounds = screenshotRequest.getBoundsInScreen(); @@ -145,7 +193,9 @@ public class TakeScreenshotService extends Service { private void sendComplete(Messenger target) { try { - Log.d(TAG, "sendComplete: " + target); + if (DEBUG_CALLBACK) { + Log.d(TAG, "sendComplete: " + target); + } target.send(Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE)); } catch (RemoteException e) { Log.d(TAG, "ignored remote exception", e); @@ -154,7 +204,9 @@ public class TakeScreenshotService extends Service { private void reportUri(Messenger target, Uri uri) { try { - Log.d(TAG, "reportUri: " + target + " -> " + uri); + if (DEBUG_CALLBACK) { + Log.d(TAG, "reportUri: " + target + " -> " + uri); + } target.send(Message.obtain(null, SCREENSHOT_MSG_URI, uri)); } catch (RemoteException e) { Log.d(TAG, "ignored remote exception", e); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 3765e5a26e8e..5259aa968825 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -117,6 +117,8 @@ public class KeyguardIndicationController implements StateListener, private boolean mPowerPluggedIn; private boolean mPowerPluggedInWired; private boolean mPowerCharged; + private boolean mBatteryOverheated; + private boolean mEnableBatteryDefender; private int mChargingSpeed; private int mChargingWattage; private int mBatteryLevel; @@ -410,7 +412,7 @@ public class KeyguardIndicationController implements StateListener, } else if (!TextUtils.isEmpty(mAlignmentIndication)) { mTextView.switchIndication(mAlignmentIndication); mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); - } else if (mPowerPluggedIn) { + } else if (mPowerPluggedIn || mEnableBatteryDefender) { String indication = computePowerIndication(); if (animate) { animateText(mTextView, indication); @@ -430,7 +432,7 @@ public class KeyguardIndicationController implements StateListener, String trustManagedIndication = getTrustManagedIndication(); String powerIndication = null; - if (mPowerPluggedIn) { + if (mPowerPluggedIn || mEnableBatteryDefender) { powerIndication = computePowerIndication(); } @@ -465,7 +467,7 @@ public class KeyguardIndicationController implements StateListener, mTextView.switchIndication(mAlignmentIndication); isError = true; hideIndication = !mBatteryPresent; - } else if (mPowerPluggedIn) { + } else if (mPowerPluggedIn || mEnableBatteryDefender) { if (DEBUG_CHARGING_SPEED) { powerIndication += ", " + (mChargingWattage / 1000) + " mW"; } @@ -545,8 +547,14 @@ public class KeyguardIndicationController implements StateListener, return mContext.getResources().getString(R.string.keyguard_charged); } - final boolean hasChargingTime = mChargingTimeRemaining > 0; int chargingId; + String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f); + if (mBatteryOverheated) { + chargingId = R.string.keyguard_plugged_in_charging_limited; + return mContext.getResources().getString(chargingId, percentage); + } + + final boolean hasChargingTime = mChargingTimeRemaining > 0; if (mPowerPluggedInWired) { switch (mChargingSpeed) { case BatteryStatus.CHARGING_FAST: @@ -571,8 +579,6 @@ public class KeyguardIndicationController implements StateListener, : R.string.keyguard_plugged_in_wireless; } - String percentage = NumberFormat.getPercentInstance() - .format(mBatteryLevel / 100f); if (hasChargingTime) { String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes( mContext, mChargingTimeRemaining); @@ -692,6 +698,8 @@ public class KeyguardIndicationController implements StateListener, mChargingSpeed = status.getChargingSpeed(mContext); mBatteryLevel = status.level; mBatteryPresent = status.present; + mBatteryOverheated = status.isOverheated(); + mEnableBatteryDefender = mBatteryOverheated && status.isPluggedIn(); try { mChargingTimeRemaining = mPowerPluggedIn ? mBatteryInfo.computeChargeTimeRemaining() : -1; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 3c4830272099..7eb921b9e6bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -335,7 +335,7 @@ public class NotificationLockscreenUserManagerImpl implements } NotificationEntry visibleEntry = getEntryManager().getActiveNotificationUnfiltered(key); return isLockscreenPublicMode(mCurrentUserId) && visibleEntry != null - && visibleEntry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET; + && visibleEntry.getRanking().getLockscreenVisibilityOverride() == VISIBILITY_SECRET; } public boolean shouldShowOnKeyguard(NotificationEntry entry) { @@ -513,7 +513,8 @@ public class NotificationLockscreenUserManagerImpl implements } NotificationEntry entry = getEntryManager().getActiveNotificationUnfiltered(key); return entry != null - && entry.getRanking().getVisibilityOverride() == Notification.VISIBILITY_PRIVATE; + && entry.getRanking().getLockscreenVisibilityOverride() + == Notification.VISIBILITY_PRIVATE; } private void updateCurrentProfilesCache() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index b1c6f535ba87..23d5369833c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -120,7 +120,7 @@ public class KeyguardCoordinator implements Coordinator { // notifications to show in public mode if (mLockscreenUserManager.isLockscreenPublicMode(currUserId) || mLockscreenUserManager.isLockscreenPublicMode(notifUserId)) { - if (entry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET) { + if (entry.getRanking().getLockscreenVisibilityOverride() == VISIBILITY_SECRET) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 37d5da24a704..7c5d4a3efee7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -20,6 +20,7 @@ import static com.android.systemui.statusbar.notification.TransformState.TRANSFO import android.app.Notification; import android.content.Context; +import android.content.res.ColorStateList; import android.util.ArraySet; import android.view.NotificationHeaderView; import android.view.NotificationTopLineView; @@ -168,9 +169,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { public void applyConversationSkin() { if (mAppNameText != null) { + final ColorStateList colors = mAppNameText.getTextColors(); mAppNameText.setTextAppearance( com.android.internal.R.style .TextAppearance_DeviceDefault_Notification_Conversation_AppName); + mAppNameText.setTextColor(colors); MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams(); layoutParams.setMarginStart(0); } @@ -189,11 +192,13 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { public void clearConversationSkin() { if (mAppNameText != null) { + final ColorStateList colors = mAppNameText.getTextColors(); final int textAppearance = Utils.getThemeAttr( mAppNameText.getContext(), com.android.internal.R.attr.notificationHeaderTextAppearance, com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info); mAppNameText.setTextAppearance(textAppearance); + mAppNameText.setTextColor(colors); MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams(); final int marginStart = mAppNameText.getResources().getDimensionPixelSize( com.android.internal.R.dimen.notification_header_app_name_margin_start); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index ac3b6d26fab6..885048df13f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -82,6 +82,9 @@ public class AmbientState { private ExpandableNotificationRow mTrackedHeadsUpRow; private float mAppearFraction; + /** Tracks the state from AlertingNotificationManager#hasNotifications() */ + private boolean mHasAlertEntries; + public AmbientState( Context context, @NonNull SectionProvider sectionProvider) { @@ -365,10 +368,21 @@ public class AmbientState { mPanelTracking = panelTracking; } + public boolean hasPulsingNotifications() { + return mPulsing && mHasAlertEntries; + } + public void setPulsing(boolean hasPulsing) { mPulsing = hasPulsing; } + /** + * @return if we're pulsing in general + */ + public boolean isPulsing() { + return mPulsing; + } + public boolean isPulsing(NotificationEntry entry) { return mPulsing && entry.isAlerting(); } @@ -527,4 +541,8 @@ public class AmbientState { public float getAppearFraction() { return mAppearFraction; } + + public void setHasAlertEntries(boolean hasAlertEntries) { + mHasAlertEntries = hasAlertEntries; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java index ba03a50b0ba5..1131a65abe93 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java @@ -31,22 +31,175 @@ import com.android.systemui.statusbar.notification.ShadeViewRefactor; import com.android.systemui.statusbar.notification.row.ExpandableView; /** - * Represents the priority of a notification section and tracks first and last visible children. + * Represents the bounds of a section of the notification shade and handles animation when the + * bounds change. */ public class NotificationSection { private @PriorityBucket int mBucket; + private View mOwningView; + private Rect mBounds = new Rect(); + private Rect mCurrentBounds = new Rect(-1, -1, -1, -1); + private Rect mStartAnimationRect = new Rect(); + private Rect mEndAnimationRect = new Rect(); + private ObjectAnimator mTopAnimator = null; + private ObjectAnimator mBottomAnimator = null; private ExpandableView mFirstVisibleChild; private ExpandableView mLastVisibleChild; - NotificationSection(@PriorityBucket int bucket) { + NotificationSection(View owningView, @PriorityBucket int bucket) { + mOwningView = owningView; mBucket = bucket; } + public void cancelAnimators() { + if (mBottomAnimator != null) { + mBottomAnimator.cancel(); + } + if (mTopAnimator != null) { + mTopAnimator.cancel(); + } + } + + public Rect getCurrentBounds() { + return mCurrentBounds; + } + + public Rect getBounds() { + return mBounds; + } + + public boolean didBoundsChange() { + return !mCurrentBounds.equals(mBounds); + } + + public boolean areBoundsAnimating() { + return mBottomAnimator != null || mTopAnimator != null; + } + @PriorityBucket public int getBucket() { return mBucket; } + public void startBackgroundAnimation(boolean animateTop, boolean animateBottom) { + // Left and right bounds are always applied immediately. + mCurrentBounds.left = mBounds.left; + mCurrentBounds.right = mBounds.right; + startBottomAnimation(animateBottom); + startTopAnimation(animateTop); + } + + + @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.STATE_RESOLVER) + private void startTopAnimation(boolean animate) { + int previousEndValue = mEndAnimationRect.top; + int newEndValue = mBounds.top; + ObjectAnimator previousAnimator = mTopAnimator; + if (previousAnimator != null && previousEndValue == newEndValue) { + return; + } + if (!animate) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + int previousStartValue = mStartAnimationRect.top; + PropertyValuesHolder[] values = previousAnimator.getValues(); + values[0].setIntValues(previousStartValue, newEndValue); + mStartAnimationRect.top = previousStartValue; + mEndAnimationRect.top = newEndValue; + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + setBackgroundTop(newEndValue); + return; + } + } + if (previousAnimator != null) { + previousAnimator.cancel(); + } + ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop", + mCurrentBounds.top, newEndValue); + Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN; + animator.setInterpolator(interpolator); + animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mStartAnimationRect.top = -1; + mEndAnimationRect.top = -1; + mTopAnimator = null; + } + }); + animator.start(); + mStartAnimationRect.top = mCurrentBounds.top; + mEndAnimationRect.top = newEndValue; + mTopAnimator = animator; + } + + @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.STATE_RESOLVER) + private void startBottomAnimation(boolean animate) { + int previousStartValue = mStartAnimationRect.bottom; + int previousEndValue = mEndAnimationRect.bottom; + int newEndValue = mBounds.bottom; + ObjectAnimator previousAnimator = mBottomAnimator; + if (previousAnimator != null && previousEndValue == newEndValue) { + return; + } + if (!animate) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + values[0].setIntValues(previousStartValue, newEndValue); + mStartAnimationRect.bottom = previousStartValue; + mEndAnimationRect.bottom = newEndValue; + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + setBackgroundBottom(newEndValue); + return; + } + } + if (previousAnimator != null) { + previousAnimator.cancel(); + } + ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom", + mCurrentBounds.bottom, newEndValue); + Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN; + animator.setInterpolator(interpolator); + animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mStartAnimationRect.bottom = -1; + mEndAnimationRect.bottom = -1; + mBottomAnimator = null; + } + }); + animator.start(); + mStartAnimationRect.bottom = mCurrentBounds.bottom; + mEndAnimationRect.bottom = newEndValue; + mBottomAnimator = animator; + } + + @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.SHADE_VIEW) + private void setBackgroundTop(int top) { + mCurrentBounds.top = top; + mOwningView.invalidate(); + } + + @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.SHADE_VIEW) + private void setBackgroundBottom(int bottom) { + mCurrentBounds.bottom = bottom; + mOwningView.invalidate(); + } + public ExpandableView getFirstVisibleChild() { return mFirstVisibleChild; } @@ -66,4 +219,93 @@ public class NotificationSection { mLastVisibleChild = child; return changed; } + + public void resetCurrentBounds() { + mCurrentBounds.set(mBounds); + } + + /** + * Returns true if {@code top} is equal to the top of this section (if not currently animating) + * or where the top of this section will be when animation completes. + */ + public boolean isTargetTop(int top) { + return (mTopAnimator == null && mCurrentBounds.top == top) + || (mTopAnimator != null && mEndAnimationRect.top == top); + } + + /** + * Returns true if {@code bottom} is equal to the bottom of this section (if not currently + * animating) or where the bottom of this section will be when animation completes. + */ + public boolean isTargetBottom(int bottom) { + return (mBottomAnimator == null && mCurrentBounds.bottom == bottom) + || (mBottomAnimator != null && mEndAnimationRect.bottom == bottom); + } + + /** + * Update the bounds of this section based on it's views + * + * @param minTopPosition the minimum position that the top needs to have + * @param minBottomPosition the minimum position that the bottom needs to have + * @return the position of the new bottom + */ + public int updateBounds(int minTopPosition, int minBottomPosition, + boolean shiftBackgroundWithFirst) { + int top = minTopPosition; + int bottom = minTopPosition; + ExpandableView firstView = getFirstVisibleChild(); + if (firstView != null) { + // Round Y up to avoid seeing the background during animation + int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView)); + // TODO: look into the already animating part + int newTop; + if (isTargetTop(finalTranslationY)) { + // we're ending up at the same location as we are now, let's just skip the + // animation + newTop = finalTranslationY; + } else { + newTop = (int) Math.ceil(firstView.getTranslationY()); + } + top = Math.max(newTop, top); + if (firstView.showingPulsing()) { + // If we're pulsing, the notification can actually go below! + bottom = Math.max(bottom, finalTranslationY + + ExpandableViewState.getFinalActualHeight(firstView)); + if (shiftBackgroundWithFirst) { + mBounds.left += Math.max(firstView.getTranslation(), 0); + mBounds.right += Math.min(firstView.getTranslation(), 0); + } + } + } + top = Math.max(minTopPosition, top); + ExpandableView lastView = getLastVisibleChild(); + if (lastView != null) { + float finalTranslationY = ViewState.getFinalTranslationY(lastView); + int finalHeight = ExpandableViewState.getFinalActualHeight(lastView); + // Round Y down to avoid seeing the background during animation + int finalBottom = (int) Math.floor( + finalTranslationY + finalHeight - lastView.getClipBottomAmount()); + int newBottom; + if (isTargetBottom(finalBottom)) { + // we're ending up at the same location as we are now, lets just skip the animation + newBottom = finalBottom; + } else { + newBottom = (int) (lastView.getTranslationY() + lastView.getActualHeight() + - lastView.getClipBottomAmount()); + // The background can never be lower than the end of the last view + minBottomPosition = (int) Math.min( + lastView.getTranslationY() + lastView.getActualHeight(), + minBottomPosition); + } + bottom = Math.max(bottom, Math.max(newBottom, minBottomPosition)); + } + bottom = Math.max(top, bottom); + mBounds.top = top; + mBounds.bottom = bottom; + return bottom; + } + + public boolean needsBackground() { + return mFirstVisibleChild != null && mBucket != BUCKET_MEDIA_CONTROLS; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index 36c5419b7399..4f7e14ba4a4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -126,7 +126,7 @@ class NotificationSectionsManager @Inject internal constructor( fun createSectionsForBuckets(): Array<NotificationSection> = sectionsFeatureManager.getNotificationBuckets() - .map { NotificationSection(it) } + .map { NotificationSection(parent, it) } .toTypedArray() /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 5cc17a08504f..4487142e8907 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -38,9 +38,12 @@ import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PointF; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.os.Bundle; import android.os.UserHandle; @@ -69,6 +72,7 @@ import android.widget.OverScroller; import android.widget.ScrollView; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.graphics.ColorUtils; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.keyguard.KeyguardSliceView; @@ -153,6 +157,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private ExpandHelper mExpandHelper; private NotificationSwipeHelper mSwipeHelper; private int mCurrentStackHeight = Integer.MAX_VALUE; + private final Paint mBackgroundPaint = new Paint(); + private final boolean mShouldDrawNotificationBackground; private boolean mHighPriorityBeforeSpeedBump; private boolean mDismissRtl; @@ -251,6 +257,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable protected FooterView mFooterView; protected EmptyShadeView mEmptyShadeView; private boolean mDismissAllInProgress; + private boolean mFadeNotificationsOnDismiss; private FooterDismissListener mFooterDismissListener; private boolean mFlingAfterUpEvent; @@ -320,6 +327,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } }; private NotificationSection[] mSections; + private boolean mAnimateNextBackgroundTop; + private boolean mAnimateNextBackgroundBottom; + private boolean mAnimateNextSectionBoundsChange; + private int mBgColor; private float mDimAmount; private ValueAnimator mDimAnimator; private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>(); @@ -330,14 +341,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } }; private ValueAnimator.AnimatorUpdateListener mDimUpdateListener - = animation -> setDimAmount((Float) animation.getAnimatedValue()); + = new ValueAnimator.AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + setDimAmount((Float) animation.getAnimatedValue()); + } + }; protected ViewGroup mQsContainer; private boolean mContinuousShadowUpdate; + private boolean mContinuousBackgroundUpdate; private ViewTreeObserver.OnPreDrawListener mShadowUpdater = () -> { updateViewShadows(); return true; }; + private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> { + updateBackground(); + return true; + }; private Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> { float endY = view.getTranslationY() + view.getActualHeight(); float otherEndY = otherView.getTranslationY() + otherView.getActualHeight(); @@ -356,7 +378,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (mAmbientState.isHiddenAtAll()) { float xProgress = mHideXInterpolator.getInterpolation( (1 - mLinearHideAmount) * mBackgroundXFactor); - outline.setRoundRect(mOutlineAnimationRect, + outline.setRoundRect(mBackgroundAnimationRect, MathUtils.lerp(mCornerRadius / 2.0f, mCornerRadius, xProgress)); outline.setAlpha(1.0f - mAmbientState.getHideAmount()); @@ -365,6 +387,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } }; + private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); private boolean mPulsing; private boolean mScrollable; private View mForcedScroll; @@ -397,6 +420,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private boolean mInHeadsUpPinnedMode; private boolean mHeadsUpAnimatingAway; private int mStatusBarState; + private int mCachedBackgroundColor; private boolean mHeadsUpGoingAwayAnimationsAllowed = true; private Runnable mReflingAndAnimateScroll = () -> { if (ANCHOR_SCROLLING) { @@ -406,7 +430,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable }; private int mCornerRadius; private int mSidePaddings; - private final Rect mOutlineAnimationRect = new Rect(); + private final Rect mBackgroundAnimationRect = new Rect(); private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); private int mHeadsUpInset; private HeadsUpAppearanceController mHeadsUpAppearanceController; @@ -426,6 +450,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final NotificationSectionsManager mSectionsManager; private ForegroundServiceDungeonView mFgsSectionView; + private boolean mAnimateBottomOnLayout; private float mLastSentAppear; private float mLastSentExpandedHeight; private boolean mWillExpand; @@ -436,6 +461,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private boolean mKeyguardMediaControllorVisible; private NotificationEntry mTopHeadsUpEntry; + private long mNumHeadsUp; private NotificationStackScrollLayoutController.TouchHandler mTouchHandler; private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener = @@ -502,6 +528,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mSections = mSectionsManager.createSectionsForBuckets(); mAmbientState = new AmbientState(context, mSectionsManager); + mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground).getDefaultColor(); int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback, @@ -510,9 +537,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mExpandHelper.setScrollAdapter(mScrollAdapter); mStackScrollAlgorithm = createStackScrollAlgorithm(context); + mShouldDrawNotificationBackground = + res.getBoolean(R.bool.config_drawNotificationBackground); setOutlineProvider(mOutlineProvider); - setWillNotDraw(!DEBUG); + boolean willDraw = mShouldDrawNotificationBackground || DEBUG; + setWillNotDraw(!willDraw); + mBackgroundPaint.setAntiAlias(true); if (DEBUG) { mDebugPaint = new Paint(); mDebugPaint.setColor(0xffff0000); @@ -557,7 +588,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * @return the height at which we will wake up when pulsing */ public float getWakeUpHeight() { - ExpandableView firstChild = getFirstExpandableView(); + ExpandableView firstChild = getFirstChildWithBackground(); if (firstChild != null) { if (mKeyguardBypassEnabledProvider.getBypassEnabled()) { return firstChild.getHeadsUpHeightWithoutHeader(); @@ -608,11 +639,22 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } void updateBgColor() { + mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground).getDefaultColor(); + updateBackgroundDimming(); mShelf.onUiModeChanged(); } @ShadeViewRefactor(RefactorComponent.DECORATOR) protected void onDraw(Canvas canvas) { + if (mShouldDrawNotificationBackground + && (mSections[0].getCurrentBounds().top + < mSections[mSections.length - 1].getCurrentBounds().bottom + || mAmbientState.isDozing())) { + drawBackground(canvas); + } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) { + drawHeadsUpBackground(canvas); + } + if (DEBUG) { int y = mTopPadding; canvas.drawLine(0, y, getWidth(), y, mDebugPaint); @@ -647,6 +689,160 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } + @ShadeViewRefactor(RefactorComponent.DECORATOR) + private void drawBackground(Canvas canvas) { + int lockScreenLeft = mSidePaddings; + int lockScreenRight = getWidth() - mSidePaddings; + int lockScreenTop = mSections[0].getCurrentBounds().top; + int lockScreenBottom = mSections[mSections.length - 1].getCurrentBounds().bottom; + int hiddenLeft = getWidth() / 2; + int hiddenTop = mTopPadding; + + float yProgress = 1 - mInterpolatedHideAmount; + float xProgress = mHideXInterpolator.getInterpolation( + (1 - mLinearHideAmount) * mBackgroundXFactor); + + int left = (int) MathUtils.lerp(hiddenLeft, lockScreenLeft, xProgress); + int right = (int) MathUtils.lerp(hiddenLeft, lockScreenRight, xProgress); + int top = (int) MathUtils.lerp(hiddenTop, lockScreenTop, yProgress); + int bottom = (int) MathUtils.lerp(hiddenTop, lockScreenBottom, yProgress); + mBackgroundAnimationRect.set( + left, + top, + right, + bottom); + + int backgroundTopAnimationOffset = top - lockScreenTop; + // TODO(kprevas): this may not be necessary any more since we don't display the shelf in AOD + boolean anySectionHasVisibleChild = false; + for (NotificationSection section : mSections) { + if (section.needsBackground()) { + anySectionHasVisibleChild = true; + break; + } + } + boolean shouldDrawBackground; + if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) { + shouldDrawBackground = isPulseExpanding(); + } else { + shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild; + } + if (shouldDrawBackground) { + drawBackgroundRects(canvas, left, right, top, backgroundTopAnimationOffset); + } + + updateClipping(); + } + + /** + * Draws round rects for each background section. + * + * We want to draw a round rect for each background section as defined by {@link #mSections}. + * However, if two sections are directly adjacent with no gap between them (e.g. on the + * lockscreen where the shelf can appear directly below the high priority section, or while + * scrolling the shade so that the top of the shelf is right at the bottom of the high priority + * section), we don't want to round the adjacent corners. + * + * Since {@link Canvas} doesn't provide a way to draw a half-rounded rect, this means that we + * need to coalesce the backgrounds for adjacent sections and draw them as a single round rect. + * This method tracks the top of each rect we need to draw, then iterates through the visible + * sections. If a section is not adjacent to the previous section, we draw the previous rect + * behind the sections we've accumulated up to that point, then start a new rect at the top of + * the current section. When we're done iterating we will always have one rect left to draw. + */ + private void drawBackgroundRects(Canvas canvas, int left, int right, int top, + int animationYOffset) { + int backgroundRectTop = top; + int lastSectionBottom = + mSections[0].getCurrentBounds().bottom + animationYOffset; + int currentLeft = left; + int currentRight = right; + boolean first = true; + for (NotificationSection section : mSections) { + if (!section.needsBackground()) { + continue; + } + int sectionTop = section.getCurrentBounds().top + animationYOffset; + int ownLeft = Math.min(Math.max(left, section.getCurrentBounds().left), right); + int ownRight = Math.max(Math.min(right, section.getCurrentBounds().right), ownLeft); + // If sections are directly adjacent to each other, we don't want to draw them + // as separate roundrects, as the rounded corners right next to each other look + // bad. + if (sectionTop - lastSectionBottom > DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX + || ((currentLeft != ownLeft || currentRight != ownRight) && !first)) { + canvas.drawRoundRect(currentLeft, + backgroundRectTop, + currentRight, + lastSectionBottom, + mCornerRadius, mCornerRadius, mBackgroundPaint); + backgroundRectTop = sectionTop; + } + currentLeft = ownLeft; + currentRight = ownRight; + lastSectionBottom = + section.getCurrentBounds().bottom + animationYOffset; + first = false; + } + canvas.drawRoundRect(currentLeft, + backgroundRectTop, + currentRight, + lastSectionBottom, + mCornerRadius, mCornerRadius, mBackgroundPaint); + } + + private void drawHeadsUpBackground(Canvas canvas) { + int left = mSidePaddings; + int right = getWidth() - mSidePaddings; + + float top = getHeight(); + float bottom = 0; + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() != View.GONE + && child instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0 + && row.getProvider().shouldShowGutsOnSnapOpen()) { + top = Math.min(top, row.getTranslationY()); + bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight()); + } + } + } + + if (top < bottom) { + canvas.drawRoundRect( + left, top, right, bottom, + mCornerRadius, mCornerRadius, mBackgroundPaint); + } + } + + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + void updateBackgroundDimming() { + // No need to update the background color if it's not being drawn. + if (!mShouldDrawNotificationBackground) { + return; + } + final boolean newFlowHideShelf = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* on by default */) == 1; + if (newFlowHideShelf) { + mBackgroundPaint.setColor(Color.TRANSPARENT); + invalidate(); + return; + } + // Interpolate between semi-transparent notification panel background color + // and white AOD separator. + float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */, + mLinearHideAmount); + int color = ColorUtils.blendARGB(mBgColor, Color.WHITE, colorInterpolation); + + if (mCachedBackgroundColor != color) { + mCachedBackgroundColor = color; + mBackgroundPaint.setColor(color); + invalidate(); + } + } + private void reinitView() { initView(getContext(), mKeyguardBypassEnabledProvider, mSwipeHelper); } @@ -781,7 +977,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateContentHeight(); clampScrollPosition(); requestChildrenUpdate(); - updateFirstAndLastExpandableView(); + updateFirstAndLastBackgroundViews(); updateAlgorithmLayoutMinHeight(); updateOwnTranslationZ(); } @@ -852,6 +1048,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private void onPreDrawDuringAnimation() { mShelf.updateAppearance(); updateClippingToTopRoundedCorner(); + if (!mNeedsAnimation && !mChildrenUpdateRequested) { + updateBackground(); + } } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -2168,6 +2367,131 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + private void updateBackground() { + // No need to update the background color if it's not being drawn. + if (!mShouldDrawNotificationBackground) { + return; + } + + updateBackgroundBounds(); + if (didSectionBoundsChange()) { + boolean animate = mAnimateNextSectionBoundsChange || mAnimateNextBackgroundTop + || mAnimateNextBackgroundBottom || areSectionBoundsAnimating(); + if (!isExpanded()) { + abortBackgroundAnimators(); + animate = false; + } + if (animate) { + startBackgroundAnimation(); + } else { + for (NotificationSection section : mSections) { + section.resetCurrentBounds(); + } + invalidate(); + } + } else { + abortBackgroundAnimators(); + } + mAnimateNextBackgroundTop = false; + mAnimateNextBackgroundBottom = false; + mAnimateNextSectionBoundsChange = false; + } + + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) + private void abortBackgroundAnimators() { + for (NotificationSection section : mSections) { + section.cancelAnimators(); + } + } + + private boolean didSectionBoundsChange() { + for (NotificationSection section : mSections) { + if (section.didBoundsChange()) { + return true; + } + } + return false; + } + + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) + private boolean areSectionBoundsAnimating() { + for (NotificationSection section : mSections) { + if (section.areBoundsAnimating()) { + return true; + } + } + return false; + } + + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) + private void startBackgroundAnimation() { + // TODO(kprevas): do we still need separate fields for top/bottom? + // or can each section manage its own animation state? + NotificationSection firstVisibleSection = getFirstVisibleSection(); + NotificationSection lastVisibleSection = getLastVisibleSection(); + for (NotificationSection section : mSections) { + section.startBackgroundAnimation( + section == firstVisibleSection + ? mAnimateNextBackgroundTop + : mAnimateNextSectionBoundsChange, + section == lastVisibleSection + ? mAnimateNextBackgroundBottom + : mAnimateNextSectionBoundsChange); + } + } + + /** + * Update the background bounds to the new desired bounds + */ + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + private void updateBackgroundBounds() { + int left = mSidePaddings; + int right = getWidth() - mSidePaddings; + for (NotificationSection section : mSections) { + section.getBounds().left = left; + section.getBounds().right = right; + } + + if (!mIsExpanded) { + for (NotificationSection section : mSections) { + section.getBounds().top = 0; + section.getBounds().bottom = 0; + } + return; + } + int minTopPosition; + NotificationSection lastSection = getLastVisibleSection(); + boolean onKeyguard = mStatusBarState == StatusBarState.KEYGUARD; + if (!onKeyguard) { + minTopPosition = (int) (mTopPadding + mStackTranslation); + } else if (lastSection == null) { + minTopPosition = mTopPadding; + } else { + // The first sections could be empty while there could still be elements in later + // sections. The position of these first few sections is determined by the position of + // the first visible section. + NotificationSection firstVisibleSection = getFirstVisibleSection(); + firstVisibleSection.updateBounds(0 /* minTopPosition*/, 0 /* minBottomPosition */, + false /* shiftPulsingWithFirst */); + minTopPosition = firstVisibleSection.getBounds().top; + } + boolean shiftPulsingWithFirst = mNumHeadsUp <= 1 + && (mAmbientState.isDozing() + || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard)); + for (NotificationSection section : mSections) { + int minBottomPosition = minTopPosition; + if (section == lastSection) { + // We need to make sure the section goes all the way to the shelf + minBottomPosition = (int) (ViewState.getFinalTranslationY(mShelf) + + mShelf.getIntrinsicHeight()); + } + minTopPosition = section.updateBounds(minTopPosition, minBottomPosition, + shiftPulsingWithFirst); + shiftPulsingWithFirst = false; + } + } + private NotificationSection getFirstVisibleSection() { for (NotificationSection section : mSections) { if (section.getFirstVisibleChild() != null) { @@ -2188,7 +2512,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - private ExpandableView getLastExpandableView() { + private ExpandableView getLastChildWithBackground() { int childCount = getChildCount(); for (int i = childCount - 1; i >= 0; i--) { ExpandableView child = (ExpandableView) getChildAt(i); @@ -2201,7 +2525,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - private ExpandableView getFirstExpandableView() { + private ExpandableView getFirstChildWithBackground() { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { ExpandableView child = (ExpandableView) getChildAt(i); @@ -2214,7 +2538,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } //TODO: We shouldn't have to generate this list every time - private List<ExpandableView> getExpandableViewList() { + private List<ExpandableView> getChildrenWithBackground() { ArrayList<ExpandableView> children = new ArrayList<>(); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { @@ -2752,13 +3076,32 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void updateFirstAndLastExpandableView() { - ExpandableView lastChild = getLastExpandableView(); - mSectionsManager.updateFirstAndLastViewsForAllSections( - mSections, getExpandableViewList()); + private void updateFirstAndLastBackgroundViews() { + NotificationSection firstSection = getFirstVisibleSection(); + NotificationSection lastSection = getLastVisibleSection(); + ExpandableView previousFirstChild = + firstSection == null ? null : firstSection.getFirstVisibleChild(); + ExpandableView previousLastChild = + lastSection == null ? null : lastSection.getLastVisibleChild(); + + ExpandableView firstChild = getFirstChildWithBackground(); + ExpandableView lastChild = getLastChildWithBackground(); + boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsForAllSections( + mSections, getChildrenWithBackground()); + + if (mAnimationsEnabled && mIsExpanded) { + mAnimateNextBackgroundTop = firstChild != previousFirstChild; + mAnimateNextBackgroundBottom = lastChild != previousLastChild || mAnimateBottomOnLayout; + mAnimateNextSectionBoundsChange = sectionViewsChanged; + } else { + mAnimateNextBackgroundTop = false; + mAnimateNextBackgroundBottom = false; + mAnimateNextSectionBoundsChange = false; + } mAmbientState.setLastVisibleBackgroundChild(lastChild); // TODO: Refactor SectionManager and put the RoundnessManager there. mController.getNoticationRoundessManager().updateRoundedChildren(mSections); + mAnimateBottomOnLayout = false; invalidate(); } @@ -2920,6 +3263,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable setAnimationRunning(true); mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay); mAnimationEvents.clear(); + updateBackground(); updateViewShadows(); updateClippingToTopRoundedCorner(); } else { @@ -4004,6 +4348,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private void setDimAmount(float dimAmount) { mDimAmount = dimAmount; + updateBackgroundDimming(); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -4072,6 +4417,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } runAnimationFinishedRunnables(); setAnimationRunning(false); + updateBackground(); updateViewShadows(); updateClippingToTopRoundedCorner(); } @@ -4200,6 +4546,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable invalidateOutline(); } updateAlgorithmHeightAndPadding(); + updateBackgroundDimming(); requestChildrenUpdate(); updateOwnTranslationZ(); } @@ -5080,6 +5427,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ public void setDozeAmount(float dozeAmount) { mAmbientState.setDozeAmount(dozeAmount); + updateContinuousBackgroundDrawing(); requestChildrenUpdate(); } @@ -5117,6 +5465,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } void setAnimateBottomOnLayout(boolean animateBottomOnLayout) { + mAnimateBottomOnLayout = animateBottomOnLayout; } public void setOnPulseHeightChangedListener(Runnable listener) { @@ -5149,14 +5498,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable void onSwipeBegin() { requestDisallowInterceptTouchEvent(true); + updateFirstAndLastBackgroundViews(); updateContinuousShadowDrawing(); + updateContinuousBackgroundDrawing(); requestChildrenUpdate(); } + void onSwipeEnd() { + updateFirstAndLastBackgroundViews(); + } + void setTopHeadsUpEntry(NotificationEntry topEntry) { mTopHeadsUpEntry = topEntry; } + void setNumHeadsUp(long numHeadsUp) { + mNumHeadsUp = numHeadsUp; + mAmbientState.setHasAlertEntries(numHeadsUp > 0); + } + public boolean getIsExpanded() { return mIsExpanded; } @@ -5271,6 +5631,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mSectionsManager.updateSectionBoundaries(reason); } + boolean isSilkDismissEnabled() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* enabled by default */) == 1; + } + + void updateContinuousBackgroundDrawing() { + if (isSilkDismissEnabled()) { + return; + } + boolean continuousBackground = !mAmbientState.isFullyAwake() + && mSwipeHelper.isSwiping(); + if (continuousBackground != mContinuousBackgroundUpdate) { + mContinuousBackgroundUpdate = continuousBackground; + if (continuousBackground) { + getViewTreeObserver().addOnPreDrawListener(mBackgroundUpdater); + } else { + getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater); + } + } + } + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) void updateContinuousShadowDrawing() { boolean continuousShadowUpdate = mAnimationRunning diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 8a0330912502..c2d030b132db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -395,6 +395,7 @@ public class NotificationStackScrollLayoutController { if (mView.getDismissAllInProgress()) { return; } + mView.onSwipeEnd(); if (view instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) view; if (row.isHeadsUp()) { @@ -454,6 +455,7 @@ public class NotificationStackScrollLayoutController { @Override public void onChildSnappedBack(View animView, float targetLeft) { + mView.onSwipeEnd(); if (animView instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) animView; if (row.isPinned() && !canChildBeDismissed(row) @@ -517,7 +519,9 @@ public class NotificationStackScrollLayoutController { @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { + long numEntries = mHeadsUpManager.getAllEntries().count(); NotificationEntry topEntry = mHeadsUpManager.getTopEntry(); + mView.setNumHeadsUp(numEntries); mView.setTopHeadsUpEntry(topEntry); mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */); } @@ -661,6 +665,8 @@ public class NotificationStackScrollLayoutController { mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed); mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener); + mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming); + mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); mFadeNotificationsOnDismiss = // TODO: this should probably be injected directly diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 9854f5450df1..4ca9c5db013c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -289,6 +289,8 @@ public class KeyguardBouncer { SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN); mDismissCallbackRegistry.notifyDismissCancelled(); } + mExpansion = EXPANSION_HIDDEN; + dispatchExpansionChanged(); mIsScrimmed = false; mFalsingCollector.onBouncerHidden(); mCallback.onBouncerVisiblityChanged(false /* shown */); @@ -377,6 +379,7 @@ public class KeyguardBouncer { */ public void setExpansion(float fraction) { float oldExpansion = mExpansion; + boolean expansionChanged = mExpansion != fraction; mExpansion = fraction; if (mKeyguardViewController != null && !mIsAnimatingAway) { mKeyguardViewController.setExpansion(fraction); @@ -394,6 +397,10 @@ public class KeyguardBouncer { mKeyguardViewController.onStartingToHide(); } } + + if (expansionChanged) { + dispatchExpansionChanged(); + } } public boolean willDismissWithAction() { @@ -518,6 +525,12 @@ public class KeyguardBouncer { } } + private void dispatchExpansionChanged() { + for (BouncerExpansionCallback callback : mExpansionCallbacks) { + callback.onExpansionChanged(mExpansion); + } + } + public void dump(PrintWriter pw) { pw.println("KeyguardBouncer"); pw.println(" isShowing(): " + isShowing()); @@ -534,6 +547,12 @@ public class KeyguardBouncer { void onStartingToHide(); void onStartingToShow(); void onFullyHidden(); + + /** + * From 0f {@link KeyguardBouncer#EXPANSION_VISIBLE} when fully visible + * to 1f {@link KeyguardBouncer#EXPANSION_HIDDEN} when fully hidden + */ + default void onExpansionChanged(float bouncerHideAmount) {} } /** Create a {@link KeyguardBouncer} once a container and bouncer callback are available. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 0e7e2fd8173c..547a3705266a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -23,6 +23,7 @@ import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCKED; import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCK_OPEN; import static com.android.systemui.statusbar.phone.LockIcon.STATE_SCANNING_FACE; +import android.animation.ArgbEvaluator; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; @@ -38,6 +39,7 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -84,7 +86,7 @@ public class LockscreenLockIconController { private boolean mDocked; private boolean mWakeAndUnlockRunning; private boolean mShowingLaunchAffordance; - private boolean mBouncerShowing; + private float mBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN; private boolean mBouncerShowingScrimmed; private boolean mFingerprintUnlock; private int mStatusBarState = StatusBarState.SHADE; @@ -104,6 +106,8 @@ public class LockscreenLockIconController { mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); mConfigurationListener.onThemeChanged(); + + updateColor(); update(); } @@ -348,7 +352,6 @@ public class LockscreenLockIconController { */ public void attach(LockIcon lockIcon) { mLockIcon = lockIcon; - updateColor(); mLockIcon.setOnClickListener(this::handleClick); mLockIcon.setOnLongClickListener(this::handleLongClick); @@ -408,20 +411,44 @@ public class LockscreenLockIconController { /** Sets whether the bouncer is showing. */ public void setBouncerShowingScrimmed(boolean showing, boolean scrimmed) { - mBouncerShowing = showing; mBouncerShowingScrimmed = scrimmed; update(); } + /** + * Sets how hidden the bouncer is, where 0f is fully visible and 1f is fully hidden + * See {@link KeyguardBouncer#EXPANSION_VISIBLE} and {@link KeyguardBouncer#EXPANSION_HIDDEN}. + */ + public void setBouncerHideAmount(float hideAmount) { + mBouncerHiddenAmount = hideAmount; + updateColor(); + } + private void updateColor() { if (mLockIcon == null) { return; } - TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( - null, new int[]{ android.R.attr.textColorPrimary }, 0, 0); - int iconColor = typedArray.getColor(0, Color.WHITE); - typedArray.recycle(); + int iconColor = -1; + if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_VISIBLE) { + TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( + null, new int[]{ android.R.attr.textColorPrimary }, 0, 0); + iconColor = typedArray.getColor(0, Color.WHITE); + typedArray.recycle(); + } else if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_HIDDEN) { + iconColor = Utils.getColorAttrDefaultColor( + mLockIcon.getContext(), com.android.systemui.R.attr.wallpaperTextColor); + } else { + // bouncer is transitioning + TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( + null, new int[]{ android.R.attr.textColorPrimary }, 0, 0); + int bouncerIconColor = typedArray.getColor(0, Color.WHITE); + typedArray.recycle(); + int keyguardIconColor = Utils.getColorAttrDefaultColor( + mLockIcon.getContext(), com.android.systemui.R.attr.wallpaperTextColor); + iconColor = (int) new ArgbEvaluator().evaluate( + mBouncerHiddenAmount, bouncerIconColor, keyguardIconColor); + } mLockIcon.updateColor(iconColor); } @@ -520,10 +547,7 @@ public class LockscreenLockIconController { return changed; } boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked; - boolean onKeyguardWithoutBouncer = mStatusBarState == StatusBarState.KEYGUARD - && !mBouncerShowing; - boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance - || onKeyguardWithoutBouncer; + boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance; boolean fingerprintOrBypass = mFingerprintUnlock || mKeyguardBypassController.getBypassEnabled(); if (fingerprintOrBypass && !mBouncerShowingScrimmed) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 48f726225657..84eacdc41841 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -141,6 +141,7 @@ import java.util.function.Consumer; import java.util.function.Function; import javax.inject.Inject; +import javax.inject.Provider; @StatusBarComponent.StatusBarScope public class NotificationPanelViewController extends PanelViewController { @@ -185,7 +186,7 @@ public class NotificationPanelViewController extends PanelViewController { private final MetricsLogger mMetricsLogger; private final ActivityManager mActivityManager; private final ConfigurationController mConfigurationController; - private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; + private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder; private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private final NotificationIconAreaController mNotificationIconAreaController; @@ -536,7 +537,7 @@ public class NotificationPanelViewController extends PanelViewController { KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, ActivityManager activityManager, ConfigurationController configurationController, - FlingAnimationUtils.Builder flingAnimationUtilsBuilder, + Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, @@ -551,7 +552,7 @@ public class NotificationPanelViewController extends PanelViewController { MediaDataManager mediaDataManager) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, - latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager); + latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager); mView = view; mMetricsLogger = metricsLogger; mActivityManager = activityManager; @@ -690,7 +691,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void loadDimens() { super.loadDimens(); - mFlingAnimationUtils = mFlingAnimationUtilsBuilder.reset() + mFlingAnimationUtils = mFlingAnimationUtilsBuilder.get() .setMaxLengthSeconds(0.4f).build(); mStatusBarMinHeight = mResources.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); @@ -2037,8 +2038,8 @@ public class NotificationPanelViewController extends PanelViewController { maxHeight = calculatePanelHeightShade(); } maxHeight = Math.max(min, maxHeight); - if (maxHeight == 0) { - Log.wtf(TAG, "maxPanelHeight is 0. getOverExpansionAmount(): " + if (maxHeight == 0 || isNaN(maxHeight)) { + Log.wtf(TAG, "maxPanelHeight is invalid. getOverExpansionAmount(): " + getOverExpansionAmount() + ", calculatePanelHeightQsExpanded: " + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: " + calculatePanelHeightShade() + ", mStatusBarMinHeight = " 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 9e872ab65591..981f9a662deb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3794,6 +3794,14 @@ public class StatusBar extends SystemUI implements DemoMode, } /** + * Sets how hidden the bouncer is, where 0f is fully visible and 1f is fully hidden + * See {@link KeyguardBouncer#EXPANSION_VISIBLE} and {@link KeyguardBouncer#EXPANSION_HIDDEN}. + */ + public void setBouncerHideAmount(float hideAmount) { + mLockscreenLockIconController.setBouncerHideAmount(hideAmount); + } + + /** * Collapses the notification shade if it is tracking or expanded. */ public void collapseShade() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index b912614ba3e8..055b78a2c000 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -130,6 +130,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb updateStates(); updateLockIcon(); } + + @Override + public void onExpansionChanged(float hideAmount) { + mStatusBar.setBouncerHideAmount(hideAmount); + } }; private final DockManager.DockEventListener mDockEventListener = new DockManager.DockEventListener() { 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 acca953629c7..6a0ebf7b5767 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -378,8 +378,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit // We have to post the removal to the UI thread for synchronization. mMainThreadHandler.post(() -> { final Runnable removeNotification = () -> { - mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK); mClickNotifier.onNotificationClick(entry.getKey(), nv); + mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK); }; if (mPresenter.isCollapsing()) { // To avoid lags we're only performing the remove diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index adbc85b8e2e5..6c5251b02291 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.policy; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.Notification; @@ -42,6 +41,7 @@ import android.text.SpannedString; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; +import android.util.Pair; import android.view.ContentInfo; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -74,8 +74,8 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LightBarController; +import java.util.Collection; import java.util.HashMap; -import java.util.Map; import java.util.function.Consumer; /** @@ -315,8 +315,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mRemoteInputs = remoteInputs; mRemoteInput = remoteInput; mEditText.setHint(mRemoteInput.getLabel()); - mEditText.mSupportedMimeTypes = (remoteInput.getAllowedDataTypes() == null) ? null - : remoteInput.getAllowedDataTypes().toArray(new String[0]); + mEditText.setSupportedMimeTypes(remoteInput.getAllowedDataTypes()); mEntry.editedSuggestionInfo = editedSuggestionInfo; if (editedSuggestionInfo != null) { @@ -570,12 +569,13 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene */ public static class RemoteEditText extends EditText { + private final OnReceiveContentListener mOnReceiveContentListener = this::onReceiveContent; + private final Drawable mBackground; private RemoteInputView mRemoteInputView; boolean mShowImeOnInputConnection; private LightBarController mLightBarController; UserHandle mUser; - private String[] mSupportedMimeTypes; public RemoteEditText(Context context, AttributeSet attrs) { super(context, attrs); @@ -583,39 +583,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mLightBarController = Dependency.get(LightBarController.class); } - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - if (mSupportedMimeTypes != null && mSupportedMimeTypes.length > 0) { - setOnReceiveContentListener(mSupportedMimeTypes, - new OnReceiveContentListener() { - @Override - @Nullable - public ContentInfo onReceiveContent(@NonNull View view, - @NonNull ContentInfo payload) { - Map<Boolean, ContentInfo> split = payload.partition( - item -> item.getUri() != null); - ContentInfo uriItems = split.get(true); - ContentInfo remainingItems = split.get(false); - if (uriItems != null) { - ClipData clip = uriItems.getClip(); - ClipDescription description = clip.getDescription(); - if (clip.getItemCount() > 1 - || description.getMimeTypeCount() < 1 - || remainingItems != null) { - // TODO(b/172363500): Update to loop over all the items - return payload; - } - Uri contentUri = clip.getItemAt(0).getUri(); - String mimeType = description.getMimeType(0); - Intent dataIntent = mRemoteInputView - .prepareRemoteInputFromData(mimeType, contentUri); - mRemoteInputView.sendRemoteInput(dataIntent); - } - return remainingItems; - } - }); + void setSupportedMimeTypes(@Nullable Collection<String> mimeTypes) { + String[] types = null; + OnReceiveContentListener listener = null; + if (mimeTypes != null && !mimeTypes.isEmpty()) { + types = mimeTypes.toArray(new String[0]); + listener = mOnReceiveContentListener; } + setOnReceiveContentListener(types, listener); } private void defocusIfNeeded(boolean animate) { @@ -759,5 +734,28 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene setBackground(null); } } + + private ContentInfo onReceiveContent(View view, ContentInfo payload) { + Pair<ContentInfo, ContentInfo> split = + payload.partition(item -> item.getUri() != null); + ContentInfo uriItems = split.first; + ContentInfo remainingItems = split.second; + if (uriItems != null) { + ClipData clip = uriItems.getClip(); + ClipDescription description = clip.getDescription(); + if (clip.getItemCount() > 1 + || description.getMimeTypeCount() < 1 + || remainingItems != null) { + // TODO(b/172363500): Update to loop over all the items + return payload; + } + Uri contentUri = clip.getItemAt(0).getUri(); + String mimeType = description.getMimeType(0); + Intent dataIntent = + mRemoteInputView.prepareRemoteInputFromData(mimeType, contentUri); + mRemoteInputView.sendRemoteInput(dataIntent); + } + return remainingItems; + } } } diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java index 286b7c049fc7..21d700e41a40 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java @@ -35,6 +35,8 @@ import android.os.UserHandle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.Window; +import android.view.WindowManager; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.TextView; @@ -58,6 +60,9 @@ public class UsbConfirmActivity extends AlertActivity @Override public void onCreate(Bundle icicle) { + getWindow().addSystemFlags( + WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + super.onCreate(icicle); Intent intent = getIntent(); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 86ba5f1f74a9..8dea5b5a19a3 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -22,6 +22,7 @@ import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsAlgorithm; @@ -34,6 +35,7 @@ import com.android.wm.shell.pip.tv.PipController; import com.android.wm.shell.pip.tv.PipControlsView; import com.android.wm.shell.pip.tv.PipControlsViewController; import com.android.wm.shell.pip.tv.PipNotification; +import com.android.wm.shell.pip.tv.TvPipMenuController; import com.android.wm.shell.splitscreen.SplitScreen; import java.util.Optional; @@ -44,7 +46,7 @@ import dagger.Provides; /** * Dagger module for TV Pip. */ -@Module +@Module(includes = {WMShellBaseModule.class}) public abstract class TvPipModule { @WMSingleton @Provides @@ -53,6 +55,7 @@ public abstract class TvPipModule { PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipTaskOrganizer pipTaskOrganizer, + TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, PipNotification pipNotification, TaskStackListenerImpl taskStackListener, @@ -63,6 +66,7 @@ public abstract class TvPipModule { pipBoundsState, pipBoundsAlgorithm, pipTaskOrganizer, + tvPipMenuController, pipMediaController, pipNotification, taskStackListener, @@ -104,14 +108,22 @@ public abstract class TvPipModule { @WMSingleton @Provides + static TvPipMenuController providesPipTvMenuController(Context context, + PipBoundsState pipBoundsState, SystemWindows systemWindows) { + return new TvPipMenuController(context, pipBoundsState, systemWindows); + } + + @WMSingleton + @Provides static PipTaskOrganizer providePipTaskOrganizer(Context context, + TvPipMenuController tvMenuController, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, - null /* menuActivityController */, pipSurfaceTransactionHelper, splitScreenOptional, + tvMenuController, pipSurfaceTransactionHelper, splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java index f88bedd88d9f..06f1522a7256 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java @@ -41,7 +41,7 @@ import dagger.Provides; * Provides dependencies from {@link com.android.wm.shell} which could be customized among different * branches of SystemUI. */ -@Module(includes = {WMShellBaseModule.class, TvPipModule.class}) +@Module(includes = {TvPipModule.class}) public class TvWMShellModule { @WMSingleton @Provides diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index be5ed0ccbef0..71a883d20988 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -16,14 +16,19 @@ package com.android.systemui.wmshell; +import static android.os.Process.THREAD_PRIORITY_DISPLAY; + +import android.animation.AnimationHandler; import android.app.IActivityManager; import android.content.Context; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.os.Handler; +import android.os.HandlerThread; import android.view.IWindowManager; import android.view.WindowManager; +import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.dagger.WMSingleton; @@ -36,7 +41,6 @@ import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.Bubbles; -import com.android.wm.shell.common.AnimationThread; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -46,6 +50,9 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.annotations.ChoreographerSfVsync; +import com.android.wm.shell.common.annotations.ShellAnimationThread; +import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; @@ -62,6 +69,7 @@ import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.splitscreen.SplitScreen; import java.util.Optional; +import java.util.concurrent.TimeUnit; import dagger.BindsOptionalOf; import dagger.Module; @@ -74,6 +82,89 @@ import dagger.Provides; @Module public abstract class WMShellBaseModule { + private static final boolean ENABLE_SHELL_MAIN_THREAD = false; + + // + // Shell Concurrency - Components used for managing threading in the Shell and SysUI + // + + /** + * Provide a SysUI main-thread Executor. + */ + @WMSingleton + @Provides + @Main + public static ShellExecutor provideSysUIMainExecutor(@Main Handler sysuiMainHandler) { + return new HandlerExecutor(sysuiMainHandler); + } + + /** + * Shell main-thread Handler, don't use this unless really necessary (ie. need to dedupe + * multiple types of messages, etc.) + */ + @WMSingleton + @Provides + @ShellMainThread + public static Handler provideShellMainHandler(@Main Handler sysuiMainHandler) { + if (ENABLE_SHELL_MAIN_THREAD) { + HandlerThread shellMainThread = new HandlerThread("wmshell.main"); + shellMainThread.start(); + return shellMainThread.getThreadHandler(); + } + return sysuiMainHandler; + } + + /** + * Provide a Shell main-thread Executor. + */ + @WMSingleton + @Provides + @ShellMainThread + public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler shellMainHandler, + @Main ShellExecutor sysuiMainExecutor) { + if (ENABLE_SHELL_MAIN_THREAD) { + return new HandlerExecutor(shellMainHandler); + } + return sysuiMainExecutor; + } + + /** + * Provide a Shell animation-thread Executor. + */ + @WMSingleton + @Provides + @ShellAnimationThread + public static ShellExecutor provideShellAnimationExecutor() { + HandlerThread shellAnimationThread = new HandlerThread("wmshell.anim", + THREAD_PRIORITY_DISPLAY); + shellAnimationThread.start(); + return new HandlerExecutor(shellAnimationThread.getThreadHandler()); + } + + /** + * Provide a Shell animation-thread AnimationHandler. The AnimationHandler can be set on + * {@link android.animation.ValueAnimator}s and will ensure that the animation will run on + * the Shell animation-thread. + */ + @WMSingleton + @Provides + @ChoreographerSfVsync + public static AnimationHandler provideShellAnimationExecutorSfVsyncAnimationHandler( + @ShellAnimationThread ShellExecutor shellAnimationExecutor) { + try { + AnimationHandler handler = new AnimationHandler(); + shellAnimationExecutor.executeBlocking(() -> { + // This is called on the animation thread since it calls + // Choreographer.getSfInstance() which returns a thread-local Choreographer instance + // that uses the SF vsync + handler.setProvider(new SfVsyncFrameCallbackProvider()); + }, 1, TimeUnit.SECONDS); + return handler; + } catch (InterruptedException e) { + throw new RuntimeException("Failed to initialize SfVsync animation handler in 1s", e); + } + } + @WMSingleton @Provides static ShellInit provideShellInit(DisplayImeController displayImeController, @@ -139,8 +230,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static WindowManagerShellWrapper provideWindowManagerShellWrapper() { - return new WindowManagerShellWrapper(); + static WindowManagerShellWrapper provideWindowManagerShellWrapper( + @ShellMainThread ShellExecutor shellMainExecutor) { + return new WindowManagerShellWrapper(shellMainExecutor); } @WMSingleton @@ -187,9 +279,11 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue, - ShellExecutor mainExecutor, TransactionPool transactionPool, Context context) { - return new ShellTaskOrganizer(syncQueue, transactionPool, mainExecutor, - AnimationThread.instance().getExecutor(), context); + @ShellMainThread ShellExecutor shellMainExecutor, + @ShellAnimationThread ShellExecutor shellAnimationExecutor, + TransactionPool transactionPool, Context context) { + return new ShellTaskOrganizer(syncQueue, transactionPool, shellMainExecutor, + shellAnimationExecutor, context); } @WMSingleton @@ -230,12 +324,6 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static ShellExecutor provideMainShellExecutor(@Main Handler handler) { - return new HandlerExecutor(handler); - } - - @WMSingleton - @Provides static Optional<HideDisplayCutout> provideHideDisplayCutoutController(Context context, DisplayController displayController) { return Optional.ofNullable(HideDisplayCutoutController.create(context, displayController)); @@ -262,5 +350,4 @@ public abstract class WMShellBaseModule { static LetterboxConfigController provideLetterboxConfigController(Context context) { return new LetterboxConfigController(context); } - } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 7a1c05890873..281b1aa9ad7b 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -34,6 +34,7 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; @@ -41,9 +42,9 @@ import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipUiEventLogger; +import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipController; -import com.android.wm.shell.pip.phone.PipMenuActivityController; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -93,13 +94,14 @@ public class WMShellModule { static Optional<Pip> providePip(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipMediaController pipMediaController, - PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, + PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, - TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) { + TaskStackListenerImpl taskStackListener, + @ShellMainThread ShellExecutor shellMainExecutor) { return Optional.ofNullable(PipController.create(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController, - pipMenuActivityController, pipTaskOrganizer, pipTouchHandler, - windowManagerShellWrapper, taskStackListener, mainExecutor)); + phonePipMenuController, pipTaskOrganizer, pipTouchHandler, + windowManagerShellWrapper, taskStackListener, shellMainExecutor)); } @WMSingleton @@ -117,21 +119,23 @@ public class WMShellModule { @WMSingleton @Provides - static PipMenuActivityController providesPipMenuActivityController(Context context, + static PhonePipMenuController providesPipPhoneMenuController(Context context, PipMediaController pipMediaController, SystemWindows systemWindows) { - return new PipMenuActivityController(context, pipMediaController, systemWindows); + return new PhonePipMenuController(context, pipMediaController, systemWindows); } @WMSingleton @Provides static PipTouchHandler providePipTouchHandler(Context context, - PipMenuActivityController menuActivityController, PipBoundsAlgorithm pipBoundsAlgorithm, + PhonePipMenuController menuPhoneController, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, FloatingContentCoordinator floatingContentCoordinator, - PipUiEventLogger pipUiEventLogger) { - return new PipTouchHandler(context, menuActivityController, pipBoundsAlgorithm, - pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger); + PipUiEventLogger pipUiEventLogger, + @ShellMainThread ShellExecutor shellMainExecutor) { + return new PipTouchHandler(context, menuPhoneController, pipBoundsAlgorithm, + pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger, + shellMainExecutor); } @WMSingleton @@ -139,12 +143,12 @@ public class WMShellModule { static PipTaskOrganizer providePipTaskOrganizer(Context context, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, - PipMenuActivityController menuActivityController, + PhonePipMenuController menuPhoneController, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, - menuActivityController, pipSurfaceTransactionHelper, splitScreenOptional, + menuPhoneController, pipSurfaceTransactionHelper, splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 4559113bcf54..a2eaea1a37c5 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.ContentResolver; import android.content.res.Resources; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -72,6 +73,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { Resources mResources; @Mock NotificationIconAreaController mNotificationIconAreaController; + @Mock + ContentResolver mContentResolver; private KeyguardClockSwitchController mController; @@ -90,7 +93,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { mColorExtractor, mClockManager, mKeyguardSliceViewController, - mNotificationIconAreaController); + mNotificationIconAreaController, + mContentResolver); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java index a0ae35ffef00..11150432f757 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java @@ -29,20 +29,23 @@ import static com.android.systemui.accessibility.MagnificationModeSwitch.FADING_ import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.ActivityInfo; +import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; @@ -60,15 +63,18 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import org.junit.After; 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.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; + @SmallTest @RunWith(AndroidTestingRunner.class) public class MagnificationModeSwitchTest extends SysuiTestCase { @@ -81,11 +87,10 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { private AccessibilityManager mAccessibilityManager; @Mock private WindowManager mWindowManager; - @Mock private ViewPropertyAnimator mViewPropertyAnimator; private MagnificationModeSwitch mMagnificationModeSwitch; - @Captor - private ArgumentCaptor<View.OnTouchListener> mTouchListenerCaptor; + private View.OnTouchListener mTouchListener; + private List<MotionEvent> mMotionEvents = new ArrayList<>(); @Before public void setUp() throws Exception { @@ -97,9 +102,23 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager); mSpyImageView = Mockito.spy(new ImageView(mContext)); - resetMockImageViewAndAnimator(); - + mViewPropertyAnimator = Mockito.spy(mSpyImageView.animate()); + resetAndStubMockImageViewAndAnimator(); + doAnswer((invocation) -> { + mTouchListener = invocation.getArgument(0); + return null; + }).when(mSpyImageView).setOnTouchListener( + any(View.OnTouchListener.class)); mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView); + assertNotNull(mTouchListener); + } + + @After + public void tearDown() { + for (MotionEvent event:mMotionEvents) { + event.recycle(); + } + mMotionEvents.clear(); } @Test @@ -124,7 +143,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { } @Test - public void showMagnificationButton_a11yTimeout_autoFadeOut() { + public void showMagnificationButton_setA11yTimeout_postDelayedAnimationWithA11yTimeout() { final int a11yTimeout = 12345; when(mAccessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn( a11yTimeout); @@ -134,14 +153,21 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { verify(mAccessibilityManager).getRecommendedTimeoutMillis( DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, AccessibilityManager.FLAG_CONTENT_ICONS | AccessibilityManager.FLAG_CONTENT_CONTROLS); - final ArgumentCaptor<Runnable> fadeOutCaptor = ArgumentCaptor.forClass(Runnable.class); - final ArgumentCaptor<Long> fadeOutDelay = ArgumentCaptor.forClass(Long.class); - verify(mSpyImageView).postOnAnimationDelayed(fadeOutCaptor.capture(), - fadeOutDelay.capture()); - assertEquals(a11yTimeout, (long) fadeOutDelay.getValue()); + verify(mSpyImageView).postOnAnimationDelayed(any(Runnable.class), eq((long) a11yTimeout)); + } + + @Test + public void showMagnificationButton_windowMode_verifyAnimationEndAction() { + // Execute the runnable immediately to run the animation. + doAnswer((invocation) -> { + final Runnable action = invocation.getArgument(0); + action.run(); + return null; + }).when(mSpyImageView).postOnAnimationDelayed(any(Runnable.class), anyLong()); + + mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); // Verify the end action after fade-out. - fadeOutCaptor.getValue().run(); final ArgumentCaptor<Runnable> endActionCaptor = ArgumentCaptor.forClass(Runnable.class); verify(mViewPropertyAnimator).withEndAction(endActionCaptor.capture()); @@ -154,7 +180,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void onConfigurationChanged_buttonIsShowing_setImageResource() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - resetMockImageViewAndAnimator(); + resetAndStubMockImageViewAndAnimator(); mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY); @@ -165,42 +191,46 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void performSingleTap_fullscreenMode_removeViewAndChangeSettingsValue() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - resetMockImageViewAndAnimator(); + resetAndStubMockImageViewAndAnimator(); // Perform a single-tap - final View.OnTouchListener listener = mTouchListenerCaptor.getValue(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, 0, ACTION_DOWN, 100, 100, 0)); + final long downTime = SystemClock.uptimeMillis(); + mTouchListener.onTouch(mSpyImageView, + obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100)); + verify(mViewPropertyAnimator).cancel(); - resetMockImageViewAndAnimator(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout(), ACTION_UP, 100, 100, 0)); + resetAndStubMockImageViewAndAnimator(); + mTouchListener.onTouch(mSpyImageView, + obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100)); + verifyTapAction(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); } @Test - public void showMagnificationButton_performDragging_updateViewLayout() { + public void performDragging_showMagnificationButton_updateViewLayout() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - resetMockImageViewAndAnimator(); - - // Perform dragging - final View.OnTouchListener listener = mTouchListenerCaptor.getValue(); - final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop(); + resetAndStubMockImageViewAndAnimator(); final int previousMode = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, 0, ACTION_DOWN, 100, 100, 0)); + + // Perform dragging + final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10; + final long downTime = SystemClock.uptimeMillis(); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, 0, ACTION_DOWN, 100, 100)); verify(mViewPropertyAnimator).cancel(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout(), ACTION_MOVE, 100 + offset, 100, 0)); + mTouchListener.onTouch(mSpyImageView, + obtainMotionEvent(downTime, downTime, ACTION_MOVE, 100 + offset, + 100)); verify(mWindowManager).updateViewLayout(eq(mSpyImageView), any(WindowManager.LayoutParams.class)); - resetMockImageViewAndAnimator(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout() + 10, ACTION_UP, 100 + offset, 100, 0)); + resetAndStubMockImageViewAndAnimator(); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, downTime, ACTION_UP, 100 + offset, 100)); + assertModeUnchanged(previousMode); assertShowFadingAnimation(FADE_OUT_ALPHA); } @@ -208,18 +238,17 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void performSingleTapActionCanceled_showButtonAnimation() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - resetMockImageViewAndAnimator(); - - // Perform single tap - final View.OnTouchListener listener = mTouchListenerCaptor.getValue(); + resetAndStubMockImageViewAndAnimator(); final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, 0, ACTION_DOWN, 100, 100, 0)); - resetMockImageViewAndAnimator(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100, 100, 0)); + final long downTime = SystemClock.uptimeMillis(); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, downTime, ACTION_DOWN, 100, 100)); + resetAndStubMockImageViewAndAnimator(); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, downTime, ACTION_CANCEL, 100, 100)); + assertModeUnchanged(previousMode); assertShowFadingAnimation(FADE_OUT_ALPHA); } @@ -227,21 +256,21 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void performDraggingActionCanceled_showButtonAnimation() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - resetMockImageViewAndAnimator(); - - // Perform dragging - final View.OnTouchListener listener = mTouchListenerCaptor.getValue(); - final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop(); + resetAndStubMockImageViewAndAnimator(); final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, 0, ACTION_DOWN, 100, 100, 0)); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout(), ACTION_MOVE, 100 + offset, 100, 0)); - - resetMockImageViewAndAnimator(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100 + offset, 100, 0)); + + // Perform dragging + final long downTime = SystemClock.uptimeMillis(); + final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10; + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + 0, 0, ACTION_DOWN, 100, 100)); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, downTime, ACTION_MOVE, 100 + offset, 100)); + resetAndStubMockImageViewAndAnimator(); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, downTime, ACTION_CANCEL, 100 + offset, 100)); + assertModeUnchanged(previousMode); assertShowFadingAnimation(FADE_OUT_ALPHA); } @@ -266,7 +295,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void performA11yActions_showWindowModeButton_verifyTapAction() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); - resetMockImageViewAndAnimator(); + resetAndStubMockImageViewAndAnimator(); mSpyImageView.performAccessibilityAction( ACTION_CLICK.getId(), null); @@ -278,7 +307,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { public void showButton_showFadeOutAnimation_fadeOutAnimationCanceled() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); assertShowFadingAnimation(FADE_OUT_ALPHA); - resetMockImageViewAndAnimator(); + resetAndStubMockImageViewAndAnimator(); mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); @@ -327,7 +356,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { } else { // Fade-out verify(mSpyImageView).postOnAnimationDelayed(runnableCaptor.capture(), anyLong()); } - resetMockAnimator(); + resetAndStubMockAnimator(); runnableCaptor.getValue().run(); @@ -336,20 +365,15 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { verify(mViewPropertyAnimator).start(); } - private void resetMockImageViewAndAnimator() { + private void resetAndStubMockImageViewAndAnimator() { + resetAndStubMockAnimator(); Mockito.reset(mSpyImageView); - doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener( - mTouchListenerCaptor.capture()); - resetMockAnimator(); + doReturn(mViewPropertyAnimator).when(mSpyImageView).animate(); } - private void resetMockAnimator() { + private void resetAndStubMockAnimator() { Mockito.reset(mViewPropertyAnimator); - when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator); - when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator); - when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn( - mViewPropertyAnimator); - when(mSpyImageView.animate()).thenReturn(mViewPropertyAnimator); + doNothing().when(mViewPropertyAnimator).start(); } /** @@ -366,4 +390,11 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT); assertEquals(expectedMode, actualMode); } + + private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x, + float y) { + MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0); + mMotionEvents.add(event); + return event; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index f9b14ba7cd31..91144be7347d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -82,6 +82,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.text.NumberFormat; import java.util.Collections; @SmallTest @@ -546,4 +547,68 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { pluggedIndication, powerIndication); assertThat(mTextView.getText()).isEqualTo(pluggedIndication); } + + @Test + public void onRefreshBatteryInfo_chargingWithOverheat_presentChargingLimited() { + createController(); + BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, + 80 /* level */, BatteryManager.BATTERY_PLUGGED_AC, + BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */, + true /* present */); + + mController.getKeyguardCallback().onRefreshBatteryInfo(status); + mController.setVisible(true); + + String percentage = NumberFormat.getPercentInstance().format(80 / 100f); + String pluggedIndication = mContext.getString( + R.string.keyguard_plugged_in_charging_limited, percentage); + assertThat(mTextView.getText()).isEqualTo(pluggedIndication); + } + + @Test + public void onRefreshBatteryInfo_pluggedWithOverheat_presentChargingLimited() { + createController(); + BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_DISCHARGING, + 80 /* level */, BatteryManager.BATTERY_PLUGGED_AC, + BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */, + true /* present */); + + mController.getKeyguardCallback().onRefreshBatteryInfo(status); + mController.setVisible(true); + + String percentage = NumberFormat.getPercentInstance().format(80 / 100f); + String pluggedIndication = mContext.getString( + R.string.keyguard_plugged_in_charging_limited, percentage); + assertThat(mTextView.getText()).isEqualTo(pluggedIndication); + } + + @Test + public void onRefreshBatteryInfo_fullChargedWithOverheat_presentCharged() { + createController(); + BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, + 100 /* level */, BatteryManager.BATTERY_PLUGGED_AC, + BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */, + true /* present */); + + mController.getKeyguardCallback().onRefreshBatteryInfo(status); + mController.setVisible(true); + + String chargedIndication = mContext.getString(R.string.keyguard_charged); + assertThat(mTextView.getText()).isEqualTo(chargedIndication); + } + + @Test + public void onRefreshBatteryInfo_dischargingWithOverheat_presentBatteryPercentage() { + createController(); + BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_DISCHARGING, + 90 /* level */, 0 /* plugged */, BatteryManager.BATTERY_HEALTH_OVERHEAT, + 0 /* maxChargingWattage */, true /* present */); + + mController.getKeyguardCallback().onRefreshBatteryInfo(status); + mController.setDozing(true); + mController.setVisible(true); + + String percentage = NumberFormat.getPercentInstance().format(90 / 100f); + assertThat(mTextView.getText()).isEqualTo(percentage); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java index 152c51e1f9f4..ac699f7192c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java @@ -65,7 +65,7 @@ public class RankingBuilder { mKey = ranking.getKey(); mRank = ranking.getRank(); mMatchesInterruptionFilter = ranking.matchesInterruptionFilter(); - mVisibilityOverride = ranking.getVisibilityOverride(); + mVisibilityOverride = ranking.getLockscreenVisibilityOverride(); mSuppressedVisualEffects = ranking.getSuppressedVisualEffects(); mImportance = ranking.getImportance(); mExplanation = ranking.getImportanceExplanation(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index fed190388ff0..d4a94a19af4f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -270,7 +270,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mDozeParameters, mCommandQueue, mVibratorHelper, mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, mMetricsLogger, mActivityManager, mConfigurationController, - flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, + () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, mConversationNotificationManager, mMediaHiearchyManager, mBiometricUnlockController, mStatusBarKeyguardViewManager, mNotificationStackScrollLayoutController, 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 1f31fcd2a2bf..52b7b022bc8a 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 @@ -81,7 +81,9 @@ import com.android.systemui.wmshell.BubblesManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; @@ -261,11 +263,12 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { verify(mAssistManager).hideAssist(); - verify(mClickNotifier).onNotificationClick( + InOrder orderVerifier = Mockito.inOrder(mClickNotifier, mOnUserInteractionCallback); + orderVerifier.verify(mClickNotifier).onNotificationClick( eq(sbn.getKey()), any(NotificationVisibility.class)); - // Notification calls dismiss callback to remove notification due to FLAG_AUTO_CANCEL - verify(mOnUserInteractionCallback).onDismiss(mNotificationRow.getEntry(), REASON_CLICK); + orderVerifier.verify(mOnUserInteractionCallback).onDismiss(mNotificationRow.getEntry(), + REASON_CLICK); } @Test diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index d6d4e4f6c746..8f093c7e6674 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -1797,4 +1797,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ public void setTouchExplorationPassthroughRegion(int displayId, Region region) { mSystemSupport.setTouchExplorationPassthroughRegion(displayId, region); } + + @Override + public void setFocusAppearance(int strokeWidth, int color) { } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index be2f8f16a412..c6919ad24572 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1968,6 +1968,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Update the capabilities before the mode. updateMagnificationCapabilitiesSettingsChangeLocked(userState); updateMagnificationModeChangeSettingsLocked(userState); + updateFocusAppearanceDataLocked(userState); } private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) { @@ -3012,6 +3013,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + /** + * Gets the stroke width of the focus rectangle. + * @return The stroke width. + */ + public int getFocusStrokeWidth() { + synchronized (mLock) { + final AccessibilityUserState userState = getCurrentUserStateLocked(); + + return userState.getFocusStrokeWidthLocked(); + } + } + + /** + * Gets the color of the focus rectangle. + * @return The color. + */ + public int getFocusColor() { + synchronized (mLock) { + final AccessibilityUserState userState = getCurrentUserStateLocked(); + + return userState.getFocusColorLocked(); + } + } + @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; @@ -3623,4 +3648,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } } + + private void updateFocusAppearanceDataLocked(AccessibilityUserState userState) { + if (userState.mUserId != mCurrentUserId) { + return; + } + + mMainHandler.post(() -> { + broadcastToClients(userState, ignoreRemoteException(client -> { + client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(), + userState.getFocusColorLocked()); + })); + }); + + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index e48d11d17f40..5d67992316a2 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -120,6 +120,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; userState.removeServiceLocked(this); + userState.resetFocusAppearanceLocked(); + mSystemSupport.onClientChangeLocked(false); mSystemSupport.getFullScreenMagnificationController().resetAllIfNeeded(mId); mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), -1, userState.mUserId); @@ -144,6 +146,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } finally { Binder.restoreCallingIdentity(identity); } + userState.resetFocusAppearanceLocked(); mSystemSupport.onClientChangeLocked(false); } } @@ -310,6 +313,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState != null) { userState.serviceDisconnectedLocked(this); + userState.resetFocusAppearanceLocked(); } resetLocked(); mSystemSupport.getFullScreenMagnificationController().resetAllIfNeeded(mId); @@ -391,4 +395,32 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } } + + @Override + public void setFocusAppearance(int strokeWidth, int color) { + AccessibilityUserState userState = mUserStateWeakReference.get(); + if (userState == null) { + return; + } + + synchronized (mLock) { + if (!hasRightsToCurrentUserLocked()) { + return; + } + + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return; + } + + if (userState.getFocusStrokeWidthLocked() == strokeWidth + && userState.getFocusColorLocked() == color) { + return; + } + + // Sets the appearance data in the A11yUserState. + userState.setFocusAppearanceLocked(strokeWidth, color); + // Updates the appearance data in the A11yManager. + mSystemSupport.onClientChangeLocked(false); + } + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index 240c7ff061c8..90e2fdfa2f03 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -45,6 +45,7 @@ import android.util.Slog; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManagerClient; +import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; import java.io.FileDescriptor; @@ -122,6 +123,15 @@ class AccessibilityUserState { // The magnification capabilities used to know magnification mode could be switched. private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + /** The stroke width of the focus rectangle in pixels */ + private int mFocusStrokeWidth; + /** The color of the focus rectangle */ + private int mFocusColor; + // The default value of the focus stroke width. + private final int mFocusStrokeWidthDefaultValue; + // The default value of the focus color. + private final int mFocusColorDefaultValue; + private Context mContext; @SoftKeyboardShowMode @@ -140,6 +150,12 @@ class AccessibilityUserState { mUserId = userId; mContext = context; mServiceInfoChangeListener = serviceInfoChangeListener; + mFocusStrokeWidthDefaultValue = mContext.getResources().getDimensionPixelSize( + R.dimen.accessibility_focus_highlight_stroke_width); + mFocusColorDefaultValue = mContext.getResources().getColor( + R.color.accessibility_focus_highlight_color); + mFocusStrokeWidth = mFocusStrokeWidthDefaultValue; + mFocusColor = mFocusColorDefaultValue; } boolean isHandlingAccessibilityEventsLocked() { @@ -178,6 +194,7 @@ class AccessibilityUserState { mUserNonInteractiveUiTimeout = 0; mUserInteractiveUiTimeout = 0; mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + resetFocusAppearanceLocked(); } void addServiceLocked(AccessibilityServiceConnection serviceConnection) { @@ -880,4 +897,40 @@ class AccessibilityUserState { } return false; } + + /** + * Gets the stroke width of the focus rectangle. + * @return The stroke width. + */ + public int getFocusStrokeWidthLocked() { + return mFocusStrokeWidth; + } + + /** + * Gets the color of the focus rectangle. + * @return The color. + */ + public int getFocusColorLocked() { + return mFocusColor; + } + + /** + * Sets the stroke width and color of the focus rectangle. + * + * @param strokeWidth The strokeWidth of the focus rectangle. + * @param color The color of the focus rectangle. + */ + public void setFocusAppearanceLocked(int strokeWidth, int color) { + mFocusStrokeWidth = strokeWidth; + mFocusColor = color; + } + + /** + * Resets the stroke width and color of the focus rectangle to the default value. + * + */ + public void resetFocusAppearanceLocked() { + mFocusStrokeWidth = mFocusStrokeWidthDefaultValue; + mFocusColor = mFocusColorDefaultValue; + } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java index 721220729a33..d2c1bc10abb0 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java @@ -106,13 +106,16 @@ public class WindowMagnificationPromptController { final Notification.Builder notificationBuilder = new Notification.Builder(mContext, SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION); + final String message = mContext.getString(R.string.window_magnification_prompt_content); + notificationBuilder.setSmallIcon(R.drawable.ic_settings_24dp) .setContentTitle(mContext.getString(R.string.window_magnification_prompt_title)) - .setContentText(mContext.getString(R.string.window_magnification_prompt_content)) + .setContentText(message) .setLargeIcon(Icon.createWithResource(mContext, R.drawable.ic_accessibility_magnification)) .setTicker(mContext.getString(R.string.window_magnification_prompt_title)) .setOnlyAlertOnce(true) + .setStyle(new Notification.BigTextStyle().bigText(message)) .setDeleteIntent(createPendingIntent(ACTION_DISMISS)) .setContentIntent(createPendingIntent(ACTION_TURN_ON_IN_SETTINGS)) .setActions(buildTurnOnAction(), buildDismissAction()); diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index f1988e9f8b6b..449063d95770 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -19,6 +19,7 @@ package com.android.server.appwidget; import static android.content.Context.KEYGUARD_SERVICE; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; + import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.annotation.UserIdInt; @@ -102,6 +103,7 @@ import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.View; import android.widget.RemoteViews; + import com.android.internal.R; import com.android.internal.app.SuspendedAppActivity; import com.android.internal.app.UnlaunchableAppActivity; @@ -111,11 +113,14 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.widget.IRemoteViewsFactory; import com.android.server.LocalServices; import com.android.server.WidgetBackupProvider; import com.android.server.policy.IconUtilities; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -137,9 +142,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider, OnCrossProfileWidgetProvidersChangeListener { @@ -2497,85 +2499,80 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - private static void serializeProvider(XmlSerializer out, Provider p) throws IOException { + private static void serializeProvider(TypedXmlSerializer out, Provider p) throws IOException { out.startTag(null, "p"); out.attribute(null, "pkg", p.info.provider.getPackageName()); out.attribute(null, "cl", p.info.provider.getClassName()); - out.attribute(null, "tag", Integer.toHexString(p.tag)); + out.attributeIntHex(null, "tag", p.tag); if (!TextUtils.isEmpty(p.infoTag)) { out.attribute(null, "info_tag", p.infoTag); } out.endTag(null, "p"); } - private static void serializeHost(XmlSerializer out, Host host) throws IOException { + private static void serializeHost(TypedXmlSerializer out, Host host) throws IOException { out.startTag(null, "h"); out.attribute(null, "pkg", host.id.packageName); - out.attribute(null, "id", Integer.toHexString(host.id.hostId)); - out.attribute(null, "tag", Integer.toHexString(host.tag)); + out.attributeIntHex(null, "id", host.id.hostId); + out.attributeIntHex(null, "tag", host.tag); out.endTag(null, "h"); } - private static void serializeAppWidget(XmlSerializer out, Widget widget, + private static void serializeAppWidget(TypedXmlSerializer out, Widget widget, boolean saveRestoreCompleted) throws IOException { out.startTag(null, "g"); - out.attribute(null, "id", Integer.toHexString(widget.appWidgetId)); - out.attribute(null, "rid", Integer.toHexString(widget.restoredId)); - out.attribute(null, "h", Integer.toHexString(widget.host.tag)); + out.attributeIntHex(null, "id", widget.appWidgetId); + out.attributeIntHex(null, "rid", widget.restoredId); + out.attributeIntHex(null, "h", widget.host.tag); if (widget.provider != null) { - out.attribute(null, "p", Integer.toHexString(widget.provider.tag)); + out.attributeIntHex(null, "p", widget.provider.tag); } if (widget.options != null) { int minWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH); int minHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT); int maxWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH); int maxHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT); - out.attribute(null, "min_width", Integer.toHexString((minWidth > 0) ? minWidth : 0)); - out.attribute(null, "min_height", Integer.toHexString((minHeight > 0) ? minHeight : 0)); - out.attribute(null, "max_width", Integer.toHexString((maxWidth > 0) ? maxWidth : 0)); - out.attribute(null, "max_height", Integer.toHexString((maxHeight > 0) ? maxHeight : 0)); - out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt( - AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY))); + out.attributeIntHex(null, "min_width", (minWidth > 0) ? minWidth : 0); + out.attributeIntHex(null, "min_height", (minHeight > 0) ? minHeight : 0); + out.attributeIntHex(null, "max_width", (maxWidth > 0) ? maxWidth : 0); + out.attributeIntHex(null, "max_height", (maxHeight > 0) ? maxHeight : 0); + out.attributeIntHex(null, "host_category", widget.options.getInt( + AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)); if (saveRestoreCompleted) { boolean restoreCompleted = widget.options.getBoolean( AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED); - out.attribute(null, "restore_completed", Boolean.toString(restoreCompleted)); + out.attributeBoolean(null, "restore_completed", restoreCompleted); } } out.endTag(null, "g"); } - private static Bundle parseWidgetIdOptions(XmlPullParser parser) { + private static Bundle parseWidgetIdOptions(TypedXmlPullParser parser) { Bundle options = new Bundle(); - String restoreCompleted = parser.getAttributeValue(null, "restore_completed"); - if (restoreCompleted != null) { - options.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED, - Boolean.valueOf(restoreCompleted)); - } - String minWidthString = parser.getAttributeValue(null, "min_width"); - if (minWidthString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, - Integer.parseInt(minWidthString, 16)); - } - String minHeightString = parser.getAttributeValue(null, "min_height"); - if (minHeightString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, - Integer.parseInt(minHeightString, 16)); - } - String maxWidthString = parser.getAttributeValue(null, "max_width"); - if (maxWidthString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, - Integer.parseInt(maxWidthString, 16)); - } - String maxHeightString = parser.getAttributeValue(null, "max_height"); - if (maxHeightString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, - Integer.parseInt(maxHeightString, 16)); - } - String categoryString = parser.getAttributeValue(null, "host_category"); - if (categoryString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, - Integer.parseInt(categoryString, 16)); + boolean restoreCompleted = parser.getAttributeBoolean(null, "restore_completed", false); + if (restoreCompleted) { + options.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED, true); + } + int minWidth = parser.getAttributeIntHex(null, "min_width", -1); + if (minWidth != -1) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minWidth); + } + int minHeight = parser.getAttributeIntHex(null, "min_height", -1); + if (minHeight != -1) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minHeight); + } + int maxWidth = parser.getAttributeIntHex(null, "max_width", -1); + if (maxWidth != -1) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxWidth); + } + int maxHeight = parser.getAttributeIntHex(null, "max_height", -1); + if (maxHeight != -1) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight); + } + int category = parser.getAttributeIntHex(null, "host_category", + AppWidgetProviderInfo.WIDGET_CATEGORY_UNKNOWN); + if (category != AppWidgetProviderInfo.WIDGET_CATEGORY_UNKNOWN) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, category); } return options; } @@ -3080,7 +3077,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku TypedXmlSerializer out = Xml.resolveSerializer(stream); out.startDocument(null, true); out.startTag(null, "gs"); - out.attribute(null, "version", String.valueOf(CURRENT_VERSION)); + out.attributeInt(null, "version", CURRENT_VERSION); N = mProviders.size(); for (int i = 0; i < N; i++) { @@ -3149,12 +3146,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (type == XmlPullParser.START_TAG) { String tag = parser.getName(); if ("gs".equals(tag)) { - String attributeValue = parser.getAttributeValue(null, "version"); - try { - version = Integer.parseInt(attributeValue); - } catch (NumberFormatException e) { - version = 0; - } + version = parser.getAttributeInt(null, "version", 0); } else if ("p".equals(tag)) { legacyProviderIndex++; // TODO: do we need to check that this package has the same signature @@ -3193,9 +3185,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku mProviders.add(provider); } - String tagAttribute = parser.getAttributeValue(null, "tag"); - final int providerTag = !TextUtils.isEmpty(tagAttribute) - ? Integer.parseInt(tagAttribute, 16) : legacyProviderIndex; + final int providerTag = parser.getAttributeIntHex(null, "tag", + legacyProviderIndex); provider.tag = providerTag; provider.infoTag = parser.getAttributeValue(null, "info_tag"); @@ -3221,12 +3212,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (!host.zombie || mSafeMode) { // In safe mode, we don't discard the hosts we don't recognize // so that they're not pruned from our list. Otherwise, we do. - final int hostId = Integer.parseInt(parser.getAttributeValue( - null, "id"), 16); - - String tagAttribute = parser.getAttributeValue(null, "tag"); - final int hostTag = !TextUtils.isEmpty(tagAttribute) - ? Integer.parseInt(tagAttribute, 16) : legacyHostIndex; + final int hostId = parser.getAttributeIntHex(null, "id"); + final int hostTag = parser.getAttributeIntHex(null, "tag", + legacyHostIndex); host.tag = hostTag; host.id = new HostId(uid, hostId, pkg); @@ -3241,21 +3229,17 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } else if ("g".equals(tag)) { Widget widget = new Widget(); - widget.appWidgetId = Integer.parseInt(parser.getAttributeValue( - null, "id"), 16); + widget.appWidgetId = parser.getAttributeIntHex(null, "id"); setMinAppWidgetIdLocked(userId, widget.appWidgetId + 1); // restored ID is allowed to be absent - String restoredIdString = parser.getAttributeValue(null, "rid"); - widget.restoredId = (restoredIdString == null) ? 0 - : Integer.parseInt(restoredIdString, 16); + widget.restoredId = parser.getAttributeIntHex(null, "rid", 0); widget.options = parseWidgetIdOptions(parser); - final int hostTag = Integer.parseInt(parser.getAttributeValue( - null, "h"), 16); + final int hostTag = parser.getAttributeIntHex(null, "h"); String providerString = parser.getAttributeValue(null, "p"); - final int providerTag = (providerString != null) ? Integer.parseInt( - parser.getAttributeValue(null, "p"), 16) : TAG_UNDEFINED; + final int providerTag = (providerString != null) + ? parser.getAttributeIntHex(null, "p") : TAG_UNDEFINED; // We can match widgets with hosts and providers only after hosts // and providers for all users have been loaded since the widget @@ -4372,11 +4356,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } try { - XmlSerializer out = new FastXmlSerializer(); + TypedXmlSerializer out = Xml.newFastSerializer(); out.setOutput(stream, StandardCharsets.UTF_8.name()); out.startDocument(null, true); out.startTag(null, "ws"); // widget state - out.attribute(null, "version", String.valueOf(WIDGET_STATE_VERSION)); + out.attributeInt(null, "version", WIDGET_STATE_VERSION); out.attribute(null, "pkg", backedupPackage); // Remember all the providers that are currently hosted or published @@ -4464,7 +4448,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // Hosts mentioned in the widget dataset by ordinal ArrayList<Host> restoredHosts = new ArrayList<>(); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(stream, StandardCharsets.UTF_8.name()); synchronized (mLock) { @@ -4474,11 +4458,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (type == XmlPullParser.START_TAG) { final String tag = parser.getName(); if ("ws".equals(tag)) { - String version = parser.getAttributeValue(null, "version"); - - final int versionNumber = Integer.parseInt(version); + final int versionNumber = parser.getAttributeInt(null, "version"); if (versionNumber > WIDGET_STATE_VERSION) { - Slog.w(TAG, "Unable to process state version " + version); + Slog.w(TAG, "Unable to process state version " + versionNumber); return; } @@ -4520,8 +4502,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku String pkg = parser.getAttributeValue(null, "pkg"); final int uid = getUidForPackage(pkg, userId); - final int hostId = Integer.parseInt( - parser.getAttributeValue(null, "id"), 16); + final int hostId = parser.getAttributeIntHex(null, "id"); HostId id = new HostId(uid, hostId, pkg); Host h = lookupOrAddHostLocked(id); @@ -4532,17 +4513,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku + "]: {" + h.id + "}"); } } else if ("g".equals(tag)) { - int restoredId = Integer.parseInt( - parser.getAttributeValue(null, "id"), 16); - int hostIndex = Integer.parseInt( - parser.getAttributeValue(null, "h"), 16); + int restoredId = parser.getAttributeIntHex(null, "id"); + int hostIndex = parser.getAttributeIntHex(null, "h"); Host host = restoredHosts.get(hostIndex); Provider p = null; - String prov = parser.getAttributeValue(null, "p"); - if (prov != null) { + int which = parser.getAttributeIntHex(null, "p", -1); + if (which != -1) { // could have been null if the app had allocated an id // but not yet established a binding under that id - int which = Integer.parseInt(prov, 16); p = restoredProviders.get(which); } diff --git a/services/art-profile b/services/art-profile index 23a2e16bdb5d..287e6094bfe0 100644 --- a/services/art-profile +++ b/services/art-profile @@ -32073,6 +32073,32 @@ PLcom/android/server/wm/AccessibilityController;->removeObserverOfEmbeddedDispla PLcom/android/server/wm/AccessibilityController;->setMagnificationCallbacksLocked(ILcom/android/server/wm/WindowManagerInternal$MagnificationCallbacks;)Z PLcom/android/server/wm/AccessibilityController;->setMagnificationSpecLocked(ILandroid/view/MagnificationSpec;)V PLcom/android/server/wm/AccessibilityController;->setWindowsForAccessibilityCallbackLocked(ILcom/android/server/wm/WindowManagerInternal$WindowsForAccessibilityCallback;)Z +HPLcom/android/server/wm/ActivityClientController;->activityDestroyed(Landroid/os/IBinder;)V +HPLcom/android/server/wm/ActivityClientController;->activityIdle(Landroid/os/IBinder;Landroid/content/res/Configuration;Z)V +HPLcom/android/server/wm/ActivityClientController;->activityPaused(Landroid/os/IBinder;)V +PLcom/android/server/wm/ActivityClientController;->activityRelaunched(Landroid/os/IBinder;)V +HPLcom/android/server/wm/ActivityClientController;->activityResumed(Landroid/os/IBinder;)V +HPLcom/android/server/wm/ActivityClientController;->activityStopped(Landroid/os/IBinder;Landroid/os/Bundle;Landroid/os/PersistableBundle;Ljava/lang/CharSequence;)V +HPLcom/android/server/wm/ActivityClientController;->activityTopResumedStateLost()V +PLcom/android/server/wm/ActivityClientController;->ensureValidPictureInPictureActivityParamsLocked(Ljava/lang/String;Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Lcom/android/server/wm/ActivityRecord; +PLcom/android/server/wm/ActivityClientController;->enterPictureInPictureMode(Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Z +HPLcom/android/server/wm/ActivityClientController;->getActivityOptions(Landroid/os/IBinder;)Landroid/os/Bundle; +PLcom/android/server/wm/ActivityClientController;->getCallingRecord(Landroid/os/IBinder;)Lcom/android/server/wm/ActivityRecord; +PLcom/android/server/wm/ActivityClientController;->isTopOfTask(Landroid/os/IBinder;)Z +PLcom/android/server/wm/ActivityClientController;->moveActivityTaskToBack(Landroid/os/IBinder;Z)Z +PLcom/android/server/wm/ActivityClientController;->navigateUpTo(Landroid/os/IBinder;Landroid/content/Intent;ILandroid/content/Intent;)Z +HPLcom/android/server/wm/ActivityClientController;->notifyActivityDrawn(Landroid/os/IBinder;)V +HPLcom/android/server/wm/ActivityClientController;->onBackPressedOnTaskRoot(Landroid/os/IBinder;)V +PLcom/android/server/wm/ActivityClientController;->overridePendingTransition(Landroid/os/IBinder;Ljava/lang/String;II)V +PLcom/android/server/wm/ActivityClientController;->registerRemoteAnimations(Landroid/os/IBinder;Landroid/view/RemoteAnimationDefinition;)V +PLcom/android/server/wm/ActivityClientController;->releaseActivityInstance(Landroid/os/IBinder;)Z +PLcom/android/server/wm/ActivityClientController;->reportActivityFullyDrawn(Landroid/os/IBinder;Z)V +HPLcom/android/server/wm/ActivityClientController;->reportSizeConfigurations(Landroid/os/IBinder;[I[I[I)V +PLcom/android/server/wm/ActivityClientController;->setDisablePreviewScreenshots(Landroid/os/IBinder;Z)V +PLcom/android/server/wm/ActivityClientController;->setShowWhenLocked(Landroid/os/IBinder;Z)V +HPLcom/android/server/wm/ActivityClientController;->setTaskDescription(Landroid/os/IBinder;Landroid/app/ActivityManager$TaskDescription;)V +PLcom/android/server/wm/ActivityClientController;->setTurnScreenOn(Landroid/os/IBinder;Z)V +PLcom/android/server/wm/ActivityClientController;->unregisterRemoteAnimations(Landroid/os/IBinder;)V HSPLcom/android/server/wm/ActivityMetricsLogger$LaunchingState;-><init>()V HSPLcom/android/server/wm/ActivityMetricsLogger$LaunchingState;->access$000(Lcom/android/server/wm/ActivityMetricsLogger$LaunchingState;)J HSPLcom/android/server/wm/ActivityMetricsLogger$LaunchingState;->access$002(Lcom/android/server/wm/ActivityMetricsLogger$LaunchingState;J)J @@ -32859,13 +32885,6 @@ HSPLcom/android/server/wm/ActivityTaskManagerService;->access$300(Lcom/android/s HSPLcom/android/server/wm/ActivityTaskManagerService;->access$602(Lcom/android/server/wm/ActivityTaskManagerService;Lcom/android/server/wm/BackgroundActivityStartCallback;)Lcom/android/server/wm/BackgroundActivityStartCallback; PLcom/android/server/wm/ActivityTaskManagerService;->access$802(Lcom/android/server/wm/ActivityTaskManagerService;Z)Z HSPLcom/android/server/wm/ActivityTaskManagerService;->access$900(Lcom/android/server/wm/ActivityTaskManagerService;)Z -HPLcom/android/server/wm/ActivityTaskManagerService;->activityDestroyed(Landroid/os/IBinder;)V -HPLcom/android/server/wm/ActivityTaskManagerService;->activityIdle(Landroid/os/IBinder;Landroid/content/res/Configuration;Z)V -HPLcom/android/server/wm/ActivityTaskManagerService;->activityPaused(Landroid/os/IBinder;)V -PLcom/android/server/wm/ActivityTaskManagerService;->activityRelaunched(Landroid/os/IBinder;)V -HPLcom/android/server/wm/ActivityTaskManagerService;->activityResumed(Landroid/os/IBinder;)V -HPLcom/android/server/wm/ActivityTaskManagerService;->activityStopped(Landroid/os/IBinder;Landroid/os/Bundle;Landroid/os/PersistableBundle;Ljava/lang/CharSequence;)V -HPLcom/android/server/wm/ActivityTaskManagerService;->activityTopResumedStateLost()V HSPLcom/android/server/wm/ActivityTaskManagerService;->addWindowLayoutReasons(I)V HPLcom/android/server/wm/ActivityTaskManagerService;->applyUpdateLockStateLocked(Lcom/android/server/wm/ActivityRecord;)V HPLcom/android/server/wm/ActivityTaskManagerService;->applyUpdateVrModeLocked(Lcom/android/server/wm/ActivityRecord;)V @@ -32898,8 +32917,6 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->enforceNotIsolatedCaller(L HPLcom/android/server/wm/ActivityTaskManagerService;->enforceTaskPermission(Ljava/lang/String;)V PLcom/android/server/wm/ActivityTaskManagerService;->enqueueAssistContext(ILandroid/content/Intent;Ljava/lang/String;Landroid/app/IAssistDataReceiver;Landroid/os/Bundle;Landroid/os/IBinder;ZZILandroid/os/Bundle;JI)Lcom/android/server/wm/ActivityTaskManagerService$PendingAssistExtras; HSPLcom/android/server/wm/ActivityTaskManagerService;->ensureConfigAndVisibilityAfterUpdate(Lcom/android/server/wm/ActivityRecord;I)Z -PLcom/android/server/wm/ActivityTaskManagerService;->ensureValidPictureInPictureActivityParamsLocked(Ljava/lang/String;Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Lcom/android/server/wm/ActivityRecord; -PLcom/android/server/wm/ActivityTaskManagerService;->enterPictureInPictureMode(Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Z PLcom/android/server/wm/ActivityTaskManagerService;->enterPictureInPictureMode(Lcom/android/server/wm/ActivityRecord;Landroid/app/PictureInPictureParams;)Z PLcom/android/server/wm/ActivityTaskManagerService;->expireStartAsCallerTokenMsg(Landroid/os/IBinder;)V HPLcom/android/server/wm/ActivityTaskManagerService;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z @@ -32909,7 +32926,6 @@ PLcom/android/server/wm/ActivityTaskManagerService;->finishSubActivity(Landroid/ PLcom/android/server/wm/ActivityTaskManagerService;->finishVoiceTask(Landroid/service/voice/IVoiceInteractionSession;)V PLcom/android/server/wm/ActivityTaskManagerService;->forgetStartAsCallerTokenMsg(Landroid/os/IBinder;)V PLcom/android/server/wm/ActivityTaskManagerService;->getActivityClassForToken(Landroid/os/IBinder;)Landroid/content/ComponentName; -HPLcom/android/server/wm/ActivityTaskManagerService;->getActivityOptions(Landroid/os/IBinder;)Landroid/os/Bundle; HSPLcom/android/server/wm/ActivityTaskManagerService;->getActivityStartController()Lcom/android/server/wm/ActivityStartController; PLcom/android/server/wm/ActivityTaskManagerService;->getAllRootTaskInfosOnDisplay(I)Ljava/util/List; HSPLcom/android/server/wm/ActivityTaskManagerService;->getAppInfoForUser(Landroid/content/pm/ApplicationInfo;I)Landroid/content/pm/ApplicationInfo; @@ -32919,7 +32935,6 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->getAppWarningsLocked()Lcom HSPLcom/android/server/wm/ActivityTaskManagerService;->getBackgroundActivityStartCallback()Lcom/android/server/wm/BackgroundActivityStartCallback; PLcom/android/server/wm/ActivityTaskManagerService;->getCallingActivity(Landroid/os/IBinder;)Landroid/content/ComponentName; PLcom/android/server/wm/ActivityTaskManagerService;->getCallingPackage(Landroid/os/IBinder;)Ljava/lang/String; -PLcom/android/server/wm/ActivityTaskManagerService;->getCallingRecordLocked(Landroid/os/IBinder;)Lcom/android/server/wm/ActivityRecord; PLcom/android/server/wm/ActivityTaskManagerService;->getConfiguration()Landroid/content/res/Configuration; PLcom/android/server/wm/ActivityTaskManagerService;->getCurrentUserId()I HPLcom/android/server/wm/ActivityTaskManagerService;->getDeviceConfigurationInfo()Landroid/content/pm/ConfigurationInfo; @@ -32984,7 +32999,6 @@ PLcom/android/server/wm/ActivityTaskManagerService;->isKeyguardLocked()Z HPLcom/android/server/wm/ActivityTaskManagerService;->isSameApp(ILjava/lang/String;)Z HSPLcom/android/server/wm/ActivityTaskManagerService;->isSleepingLocked()Z HPLcom/android/server/wm/ActivityTaskManagerService;->isSleepingOrShuttingDownLocked()Z -PLcom/android/server/wm/ActivityTaskManagerService;->isTopOfTask(Landroid/os/IBinder;)Z HPLcom/android/server/wm/ActivityTaskManagerService;->isUidForeground(I)Z HPLcom/android/server/wm/ActivityTaskManagerService;->keyguardGoingAway(I)V PLcom/android/server/wm/ActivityTaskManagerService;->lambda$4YLTqMi21jZ51BFcKX_h_gIoeGg(Lcom/android/server/wm/ActivityTaskManagerService;Ljava/util/Locale;)V @@ -33000,33 +33014,22 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->lambda$setLockScreenShown$ PLcom/android/server/wm/ActivityTaskManagerService;->lambda$tPFRgtovnZu_2Zm4sCcLa9-oBto(Lcom/android/server/wm/ActivityTaskManagerService;Landroid/os/IBinder;)V PLcom/android/server/wm/ActivityTaskManagerService;->lambda$uDPnzqVuuoVSFA7RJcXFWsrCwrY(Lcom/android/server/wm/ActivityTaskManagerService;ILandroid/content/res/Configuration;)V PLcom/android/server/wm/ActivityTaskManagerService;->logAppTooSlow(Lcom/android/server/wm/WindowProcessController;JLjava/lang/String;)V -PLcom/android/server/wm/ActivityTaskManagerService;->moveActivityTaskToBack(Landroid/os/IBinder;Z)Z PLcom/android/server/wm/ActivityTaskManagerService;->moveTaskToFrontLocked(Landroid/app/IApplicationThread;Ljava/lang/String;IILcom/android/server/wm/SafeActivityOptions;)V -PLcom/android/server/wm/ActivityTaskManagerService;->navigateUpTo(Landroid/os/IBinder;Landroid/content/Intent;ILandroid/content/Intent;)Z -HPLcom/android/server/wm/ActivityTaskManagerService;->notifyActivityDrawn(Landroid/os/IBinder;)V -HSPLcom/android/server/wm/ActivityTaskManagerService;->notifyEnterAnimationComplete(Landroid/os/IBinder;)V -PLcom/android/server/wm/ActivityTaskManagerService;->notifyLaunchTaskBehindComplete(Landroid/os/IBinder;)V PLcom/android/server/wm/ActivityTaskManagerService;->notifyTaskPersisterLocked(Lcom/android/server/wm/Task;Z)V HSPLcom/android/server/wm/ActivityTaskManagerService;->onActivityManagerInternalAdded()V -HPLcom/android/server/wm/ActivityTaskManagerService;->onBackPressedOnTaskRoot(Landroid/os/IBinder;)V HSPLcom/android/server/wm/ActivityTaskManagerService;->onInitPowerManagement()V HPLcom/android/server/wm/ActivityTaskManagerService;->onScreenAwakeChanged(Z)V HSPLcom/android/server/wm/ActivityTaskManagerService;->onStartActivitySetDidAppSwitch()V HSPLcom/android/server/wm/ActivityTaskManagerService;->onSystemReady()V -PLcom/android/server/wm/ActivityTaskManagerService;->overridePendingTransition(Landroid/os/IBinder;Ljava/lang/String;II)V PLcom/android/server/wm/ActivityTaskManagerService;->pendingAssistExtrasTimedOut(Lcom/android/server/wm/ActivityTaskManagerService$PendingAssistExtras;)V PLcom/android/server/wm/ActivityTaskManagerService;->postFinishBooting(ZZ)V PLcom/android/server/wm/ActivityTaskManagerService;->registerRemoteAnimationForNextActivityStart(Ljava/lang/String;Landroid/view/RemoteAnimationAdapter;)V -PLcom/android/server/wm/ActivityTaskManagerService;->registerRemoteAnimations(Landroid/os/IBinder;Landroid/view/RemoteAnimationDefinition;)V HPLcom/android/server/wm/ActivityTaskManagerService;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V PLcom/android/server/wm/ActivityTaskManagerService;->relaunchReasonToString(I)Ljava/lang/String; -PLcom/android/server/wm/ActivityTaskManagerService;->releaseActivityInstance(Landroid/os/IBinder;)Z PLcom/android/server/wm/ActivityTaskManagerService;->releaseSomeActivities(Landroid/app/IApplicationThread;)V PLcom/android/server/wm/ActivityTaskManagerService;->removeRootTasksInWindowingModes([I)V PLcom/android/server/wm/ActivityTaskManagerService;->removeTask(I)Z -PLcom/android/server/wm/ActivityTaskManagerService;->reportActivityFullyDrawn(Landroid/os/IBinder;Z)V PLcom/android/server/wm/ActivityTaskManagerService;->reportAssistContextExtras(Landroid/os/IBinder;Landroid/os/Bundle;Landroid/app/assist/AssistStructure;Landroid/app/assist/AssistContent;Landroid/net/Uri;)V -HPLcom/android/server/wm/ActivityTaskManagerService;->reportSizeConfigurations(Landroid/os/IBinder;[I[I[I)V PLcom/android/server/wm/ActivityTaskManagerService;->requestAssistContextExtras(ILandroid/app/IAssistDataReceiver;Landroid/os/Bundle;Landroid/os/IBinder;ZZ)Z PLcom/android/server/wm/ActivityTaskManagerService;->requestAutofillData(Landroid/app/IAssistDataReceiver;Landroid/os/Bundle;Landroid/os/IBinder;I)Z PLcom/android/server/wm/ActivityTaskManagerService;->requestStartActivityPermissionToken(Landroid/os/IBinder;)Landroid/os/IBinder; @@ -33039,7 +33042,6 @@ PLcom/android/server/wm/ActivityTaskManagerService;->sendPutConfigurationForUser PLcom/android/server/wm/ActivityTaskManagerService;->setBooted(Z)V PLcom/android/server/wm/ActivityTaskManagerService;->setBooting(Z)V HSPLcom/android/server/wm/ActivityTaskManagerService;->setDeviceOwnerUid(I)V -PLcom/android/server/wm/ActivityTaskManagerService;->setDisablePreviewScreenshots(Landroid/os/IBinder;Z)V PLcom/android/server/wm/ActivityTaskManagerService;->setFocusedTask(I)V PLcom/android/server/wm/ActivityTaskManagerService;->setImmersive(Landroid/os/IBinder;Z)V HPLcom/android/server/wm/ActivityTaskManagerService;->setLockScreenShown(ZZ)V @@ -33047,10 +33049,7 @@ PLcom/android/server/wm/ActivityTaskManagerService;->setPictureInPictureParams(L HSPLcom/android/server/wm/ActivityTaskManagerService;->setRecentTasks(Lcom/android/server/wm/RecentTasks;)V PLcom/android/server/wm/ActivityTaskManagerService;->setRequestedOrientation(Landroid/os/IBinder;I)V HPLcom/android/server/wm/ActivityTaskManagerService;->setResumedActivityUncheckLocked(Lcom/android/server/wm/ActivityRecord;Ljava/lang/String;)V -PLcom/android/server/wm/ActivityTaskManagerService;->setShowWhenLocked(Landroid/os/IBinder;Z)V PLcom/android/server/wm/ActivityTaskManagerService;->setSplitScreenResizing(Z)V -HPLcom/android/server/wm/ActivityTaskManagerService;->setTaskDescription(Landroid/os/IBinder;Landroid/app/ActivityManager$TaskDescription;)V -PLcom/android/server/wm/ActivityTaskManagerService;->setTurnScreenOn(Landroid/os/IBinder;Z)V HSPLcom/android/server/wm/ActivityTaskManagerService;->setUsageStatsManager(Landroid/app/usage/UsageStatsManagerInternal;)V HSPLcom/android/server/wm/ActivityTaskManagerService;->setWindowManager(Lcom/android/server/wm/WindowManagerService;)V PLcom/android/server/wm/ActivityTaskManagerService;->shouldDisableNonVrUiLocked()Z @@ -33070,7 +33069,6 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->startTimeTrackingFocusedAc PLcom/android/server/wm/ActivityTaskManagerService;->stopAppSwitches()V PLcom/android/server/wm/ActivityTaskManagerService;->stopLockTaskModeInternal(Landroid/os/IBinder;Z)V PLcom/android/server/wm/ActivityTaskManagerService;->stopSystemLockTaskMode()V -PLcom/android/server/wm/ActivityTaskManagerService;->unregisterRemoteAnimations(Landroid/os/IBinder;)V HPLcom/android/server/wm/ActivityTaskManagerService;->unregisterTaskStackListener(Landroid/app/ITaskStackListener;)V HPLcom/android/server/wm/ActivityTaskManagerService;->updateActivityUsageStats(Lcom/android/server/wm/ActivityRecord;I)V HPLcom/android/server/wm/ActivityTaskManagerService;->updateBatteryStats(Lcom/android/server/wm/ActivityRecord;Z)V diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 32126987376a..33d13de8be4b 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -1138,7 +1138,7 @@ final class AutofillManagerServiceImpl final int sessionCount = mSessions.size(); for (int i = sessionCount - 1; i >= 0; i--) { final Session session = mSessions.valueAt(i); - if (session.isSavingLocked()) { + if (session.isSaveUiShowingLocked()) { if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id); session.forceRemoveFromServiceLocked(); } else { @@ -1660,7 +1660,7 @@ final class AutofillManagerServiceImpl if (sessionToRemove != null && sessionsToRemove.valueAt(i) == sessionToRemove.getActivityTokenLocked()) { - if (sessionToRemove.isSavingLocked()) { + if (sessionToRemove.isSaveUiShowingLocked()) { if (sVerbose) { Slog.v(TAG, "Session " + sessionToRemove.id + " is saving"); } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index b48d71a51ce3..9d8901adbc9c 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -39,6 +39,7 @@ import static com.android.server.autofill.Helper.toArray; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; @@ -113,6 +114,8 @@ import com.android.server.autofill.ui.PendingUi; import com.android.server.inputmethod.InputMethodManagerInternal; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -153,6 +156,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private static AtomicInteger sIdCounter = new AtomicInteger(2); + @GuardedBy("mLock") + private @SessionState int mSessionState = STATE_UNKNOWN; + + /** Session state uninitiated. */ + public static final int STATE_UNKNOWN = 0; + + /** Session is active for filling. */ + public static final int STATE_ACTIVE = 1; + + /** Session finished for filling, staying alive for saving. */ + public static final int STATE_FINISHED = 2; + + /** Session is destroyed and removed from the manager service. */ + public static final int STATE_REMOVED = 3; + + @IntDef(prefix = { "STATE_" }, value = { + STATE_UNKNOWN, + STATE_ACTIVE, + STATE_FINISHED, + STATE_REMOVED + }) + @Retention(RetentionPolicy.SOURCE) + @interface SessionState{} + + @GuardedBy("mLock") + private final SessionFlags mSessionFlags; + /** * ID of the session. * @@ -236,10 +266,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private boolean mDestroyed; - /** Whether the session is currently saving. */ - @GuardedBy("mLock") - private boolean mIsSaving; - /** * Helper used to handle state of Save UI when it must be hiding to show a custom description * link and later recovered. @@ -270,9 +296,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private final LocalLog mWtfHistory; - @GuardedBy("mLock") - private boolean mExpiredResponse; - /** * Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id. */ @@ -307,13 +330,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private ArrayList<AutofillId> mAugmentedAutofillableIds; - /** - * When {@code true}, the session was created only to handle Augmented Autofill requests (i.e., - * the session would not have existed otherwsie). - */ - @GuardedBy("mLock") - private boolean mForAugmentedAutofillOnly; - @Nullable private final AutofillInlineSessionController mInlineSessionController; @@ -327,18 +343,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // returns null, and augmented autofill is triggered, and then the user switches the input // method. Tapping on the field again will not trigger a new augmented autofill request. // This may be fixed by adding more checks such as whether mCurrentViewId is null. - if (mExpiredResponse) { + if (mSessionFlags.mExpiredResponse) { return; } if (shouldResetSessionStateOnInputMethodSwitch()) { // Set the old response expired, so the next action (ACTION_VIEW_ENTERED) can trigger // a new fill request. - mExpiredResponse = true; + mSessionFlags.mExpiredResponse = true; // Clear the augmented autofillable ids so augmented autofill will trigger again. mAugmentedAutofillableIds = null; // In case the field is augmented autofill only, we clear the current view id, so that // we won't skip view entered due to same view entered, for the augmented autofill. - if (mForAugmentedAutofillOnly) { + if (mSessionFlags.mAugmentedAutofillOnly) { mCurrentViewId = null; } } @@ -356,7 +372,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } - if (isInlineSuggestionsEnabledByAutofillProviderLocked()) { + if (mSessionFlags.mInlineSupportedByService) { return true; } @@ -370,6 +386,31 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** + * Collection of flags/booleans that helps determine Session behaviors. + */ + private final class SessionFlags { + /** Whether autofill is disabled by the service */ + @GuardedBy("mLock") + private boolean mAutofillDisabled; + + /** Whether the autofill service supports inline suggestions */ + @GuardedBy("mLock") + private boolean mInlineSupportedByService; + + /** True if session is for augmented only */ + @GuardedBy("mLock") + private boolean mAugmentedAutofillOnly; + + /** Whether the session is currently showing the SaveUi. */ + @GuardedBy("mLock") + private boolean mShowingSaveUi; + + /** Whether the current {@link FillResponse} is expired. */ + @GuardedBy("mLock") + private boolean mExpiredResponse; + } + + /** * TODO(b/151867668): improve how asynchronous data dependencies are handled, without using * CountDownLatch. */ @@ -423,7 +464,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void onHandleAssistData(Bundle resultData) throws RemoteException { if (mRemoteFillService == null) { wtf(null, "onHandleAssistData() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly); + + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); return; } // Keeps to prevent it is cleared on multiple threads. @@ -685,7 +726,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private void cancelCurrentRequestLocked() { if (mRemoteFillService == null) { wtf(null, "cancelCurrentRequestLocked() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly); + + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); return; } final int canceledRequest = mRemoteFillService.cancelCurrentRequest(); @@ -705,14 +746,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Returns whether inline suggestions are supported by Autofill provider (not augmented - * Autofill provider). - */ - private boolean isInlineSuggestionsEnabledByAutofillProviderLocked() { - return mService.isInlineSuggestionsEnabledLocked(); - } - private boolean isViewFocusedLocked(int flags) { return (flags & FLAG_VIEW_NOT_FOCUSED) == 0; } @@ -733,14 +766,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState ViewState.STATE_INITIAL, /* clearResponse= */ true); } - mExpiredResponse = false; - if (mForAugmentedAutofillOnly || mRemoteFillService == null) { + mSessionFlags.mExpiredResponse = false; + mSessionState = STATE_ACTIVE; + if (mSessionFlags.mAugmentedAutofillOnly || mRemoteFillService == null) { if (sVerbose) { Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead " - + "(mForAugmentedAutofillOnly=" + mForAugmentedAutofillOnly + + "(mForAugmentedAutofillOnly=" + mSessionFlags.mAugmentedAutofillOnly + ", flags=" + flags + ")"); } - mForAugmentedAutofillOnly = true; + mSessionFlags.mAugmentedAutofillOnly = true; triggerAugmentedAutofillLocked(flags); return; } @@ -779,7 +813,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // is also not focused. final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); - if (isInlineSuggestionsEnabledByAutofillProviderLocked() + if (mSessionFlags.mInlineSupportedByService && remoteRenderService != null && isViewFocusedLocked(flags)) { Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = @@ -849,8 +883,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mWtfHistory = wtfHistory; mComponentName = componentName; mCompatMode = compatMode; - mForAugmentedAutofillOnly = forAugmentedAutofillOnly; - setClientLocked(client); + mSessionState = STATE_ACTIVE; + synchronized (mLock) { + mSessionFlags = new SessionFlags(); + mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly; + mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked(); + setClientLocked(client); + } mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal, userId, componentName, handler, mLock, @@ -906,9 +945,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState unlinkClientVultureLocked(); mClient = IAutoFillManagerClient.Stub.asInterface(client); mClientVulture = () -> { - Slog.d(TAG, "handling death of " + mActivityToken + " when saving=" + mIsSaving); synchronized (mLock) { - if (mIsSaving) { + Slog.d(TAG, "handling death of " + mActivityToken + " when saving=" + + mSessionFlags.mShowingSaveUi); + if (mSessionFlags.mShowingSaveUi) { mUi.hideFillUi(this); } else { mUi.destroyAll(mPendingSaveUi, this, false); @@ -973,9 +1013,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mService.setLastResponse(id, response); - int sessionFinishedState = 0; final long disableDuration = response.getDisableDuration(); - if (disableDuration > 0) { + final boolean autofillDisabled = disableDuration > 0; + if (autofillDisabled) { final int flags = response.getFlags(); final boolean disableActivityOnly = (flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0; @@ -990,15 +1030,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState id, mCompatMode); } - // Although "standard" autofill is disabled, it might still trigger augmented autofill - if (triggerAugmentedAutofillLocked(requestFlags) != null) { - mForAugmentedAutofillOnly = true; - if (sDebug) { - Slog.d(TAG, "Service disabled autofill for " + mComponentName - + ", but session is kept for augmented autofill only"); + synchronized (mLock) { + mSessionFlags.mAutofillDisabled = true; + + // Although "standard" autofill is disabled, it might still trigger augmented + // autofill + if (triggerAugmentedAutofillLocked(requestFlags) != null) { + mSessionFlags.mAugmentedAutofillOnly = true; + if (sDebug) { + Slog.d(TAG, "Service disabled autofill for " + mComponentName + + ", but session is kept for augmented autofill only"); + } + return; } - return; } + if (sDebug) { final StringBuilder message = new StringBuilder("Service disabled autofill for ") .append(mComponentName) @@ -1007,14 +1053,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState TimeUtils.formatDuration(disableDuration, message); Slog.d(TAG, message.toString()); } - sessionFinishedState = AutofillManager.STATE_DISABLED_BY_SERVICE; } if (((response.getDatasets() == null || response.getDatasets().isEmpty()) && response.getAuthentication() == null) - || disableDuration > 0) { + || autofillDisabled) { // Response is "empty" from an UI point of view, need to notify client. - notifyUnavailableToClient(sessionFinishedState, /* autofillableIds= */ null); + notifyUnavailableToClient( + autofillDisabled ? AutofillManager.STATE_DISABLED_BY_SERVICE : 0, + /* autofillableIds= */ null); synchronized (mLock) { mInlineSessionController.setInlineFillUiLocked( InlineFillUi.emptyUi(mCurrentViewId)); @@ -1094,7 +1141,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void onSaveRequestSuccess(@NonNull String servicePackageName, @Nullable IntentSender intentSender) { synchronized (mLock) { - mIsSaving = false; + mSessionFlags.mShowingSaveUi = false; if (mDestroyed) { Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: " @@ -1120,7 +1167,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull String servicePackageName) { boolean showMessage = !TextUtils.isEmpty(message); synchronized (mLock) { - mIsSaving = false; + mSessionFlags.mShowingSaveUi = false; if (mDestroyed) { Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: " @@ -1246,7 +1293,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void cancelSave() { synchronized (mLock) { - mIsSaving = false; + mSessionFlags.mShowingSaveUi = false; if (mDestroyed) { Slog.w(TAG, "Call to Session#cancelSave() rejected - session: " @@ -1424,7 +1471,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // The client becomes invisible for the authentication, the response is effective. - mExpiredResponse = false; + mSessionFlags.mExpiredResponse = false; final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT); final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE); @@ -1996,6 +2043,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, Event.NO_SAVE_REASON_NONE); } + mSessionState = STATE_FINISHED; final FillResponse response = getLastResponseLocked("showSaveLocked(%s)"); final SaveInfo saveInfo = response == null ? null : response.getSaveInfo(); @@ -2267,7 +2315,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.e(TAG, "Error notifying client to set save UI state to shown: " + e); } } - mIsSaving = true; + mSessionFlags.mShowingSaveUi = true; return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false, Event.NO_SAVE_REASON_NONE); } @@ -2316,8 +2364,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Returns whether the session is currently showing the save UI */ @GuardedBy("mLock") - boolean isSavingLocked() { - return mIsSaving; + boolean isSaveUiShowingLocked() { + return mSessionFlags.mShowingSaveUi; } /** @@ -2435,7 +2483,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (mRemoteFillService == null) { wtf(null, "callSaveLocked() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly); + + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); return; } @@ -2540,7 +2588,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private boolean requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id, @NonNull ViewState viewState, int flags) { if ((flags & FLAG_MANUAL_REQUEST) != 0) { - mForAugmentedAutofillOnly = false; + mSessionFlags.mAugmentedAutofillOnly = false; if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags); requestNewFillResponseLocked(viewState, ViewState.STATE_RESTARTED_SESSION, flags); return true; @@ -2578,7 +2626,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0; } - if (mExpiredResponse) { + if (mSessionFlags.mExpiredResponse) { if (sDebug) { Slog.d(TAG, "Starting a new partition because the response has expired."); } @@ -2637,7 +2685,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } if (action == ACTION_RESPONSE_EXPIRED) { - mExpiredResponse = true; + mSessionFlags.mExpiredResponse = true; if (sDebug) { Slog.d(TAG, "Set the response has expired."); } @@ -2652,7 +2700,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState ViewState viewState = mViewStates.get(id); if (sVerbose) { Slog.v(TAG, "updateLocked(" + this.id + "): mCurrentViewId=" + mCurrentViewId - + ", mExpiredResponse=" + mExpiredResponse + ", viewState=" + viewState); + + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse + + ", viewState=" + viewState); } if (viewState == null) { @@ -2753,7 +2802,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Slog.d(TAG, "skip augmented autofill for same view."); } return; - } else if (mForAugmentedAutofillOnly && isSameViewEntered) { + } else if (mSessionFlags.mAugmentedAutofillOnly && isSameViewEntered) { // Regular autofill is disabled. if (sDebug) Slog.d(TAG, "skip augmented autofill for same view."); return; @@ -3369,9 +3418,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); if (remoteRenderService != null - && (mForAugmentedAutofillOnly - || !isInlineSuggestionsEnabledByAutofillProviderLocked() - || mExpiredResponse) + && (mSessionFlags.mAugmentedAutofillOnly + || !mSessionFlags.mInlineSupportedByService + || mSessionFlags.mExpiredResponse) && isViewFocusedLocked(flags)) { if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill"); remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback( @@ -3694,7 +3743,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public String toString() { - return "Session: [id=" + id + ", component=" + mComponentName + "]"; + return "Session: [id=" + id + ", component=" + mComponentName + + ", state=" + sessionStateAsString(mSessionState) + "]"; } @GuardedBy("mLock") @@ -3704,6 +3754,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.print(prefix); pw.print("uid: "); pw.println(uid); pw.print(prefix); pw.print("taskId: "); pw.println(taskId); pw.print(prefix); pw.print("flags: "); pw.println(mFlags); + pw.print(prefix); pw.print("state: "); pw.println(sessionStateAsString(mSessionState)); pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName); pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime); @@ -3734,7 +3785,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId); pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed); - pw.print(prefix); pw.print("mIsSaving: "); pw.println(mIsSaving); + pw.print(prefix); pw.print("mShowingSaveUi: "); pw.println(mSessionFlags.mShowingSaveUi); pw.print(prefix); pw.print("mPendingSaveUi: "); pw.println(mPendingSaveUi); final int numberViews = mViewStates.size(); pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size()); @@ -3778,7 +3829,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println( mSaveOnAllViewsInvisible); pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds); - if (mForAugmentedAutofillOnly) { + if (mSessionFlags.mAugmentedAutofillOnly) { pw.print(prefix); pw.println("For Augmented Autofill Only"); } if (mAugmentedAutofillDestroyer != null) { @@ -3967,7 +4018,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS, totalAugmentedRequests); } - if (mForAugmentedAutofillOnly) { + if (mSessionFlags.mAugmentedAutofillOnly) { log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_AUGMENTED_ONLY, 1); } mMetricsLogger.write(log); @@ -3988,9 +4039,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void forceRemoveFromServiceIfForAugmentedOnlyLocked() { if (sVerbose) { Slog.v(TAG, "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + this.id + "): " - + mForAugmentedAutofillOnly); + + mSessionFlags.mAugmentedAutofillOnly); } - if (!mForAugmentedAutofillOnly) return; + if (!mSessionFlags.mAugmentedAutofillOnly) return; forceRemoveFromServiceLocked(); } @@ -4052,6 +4103,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (remoteFillService != null) { remoteFillService.destroy(); } + mSessionState = STATE_REMOVED; } void onPendingSaveUi(int operation, @NonNull IBinder token) { @@ -4168,4 +4220,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return "UNKNOWN_" + action; } } + + private static String sessionStateAsString(@SessionState int sessionState) { + switch (sessionState) { + case STATE_UNKNOWN: + return "STATE_UNKNOWN"; + case STATE_ACTIVE: + return "STATE_ACTIVE"; + case STATE_FINISHED: + return "STATE_FINISHED"; + case STATE_REMOVED: + return "STATE_REMOVED"; + default: + return "UNKNOWN_SESSION_STATE_" + sessionState; + } + } } diff --git a/services/backup/Android.bp b/services/backup/Android.bp index b5444f485a1c..68376c5ad178 100644 --- a/services/backup/Android.bp +++ b/services/backup/Android.bp @@ -10,5 +10,5 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.backup-sources"], libs: ["services.core"], - static_libs: ["backuplib"], + static_libs: ["backuplib", "app-compat-annotations"], } diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 2ff66b564ec9..136cd22fad83 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -3030,9 +3030,11 @@ public class UserBackupManagerService { } Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning adb backup...")); + BackupEligibilityRules eligibilityRules = getEligibilityRulesForOperation( + OperationType.ADB_BACKUP); AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs, includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue, - pkgList, mScheduledBackupEligibility); + pkgList, eligibilityRules); final int token = generateRandomIntegerToken(); synchronized (mAdbBackupRestoreConfirmations) { mAdbBackupRestoreConfirmations.put(token, params); diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java index c94286ffcffa..e03150eb824f 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java @@ -24,6 +24,7 @@ import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK; import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC; import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION; +import android.app.backup.BackupManager; import android.app.backup.IFullBackupRestoreObserver; import android.content.pm.PackageManagerInternal; import android.os.ParcelFileDescriptor; @@ -104,11 +105,13 @@ public class PerformAdbRestoreTask implements Runnable { return; } + BackupEligibilityRules eligibilityRules = new BackupEligibilityRules( + mBackupManagerService.getPackageManager(), + LocalServices.getService(PackageManagerInternal.class), + mBackupManagerService.getUserId(), BackupManager.OperationType.ADB_BACKUP); FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService, null, mObserver, null, null, true, 0 /*unused*/, true, - BackupEligibilityRules.forBackup(mBackupManagerService.getPackageManager(), - LocalServices.getService(PackageManagerInternal.class), - mBackupManagerService.getUserId())); + eligibilityRules); FullRestoreEngineThread mEngineThread = new FullRestoreEngineThread(mEngine, tarInputStream); mEngineThread.run(); diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java index 73ba1f19c092..2078492e67ab 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java +++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java @@ -25,12 +25,16 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.annotation.Nullable; import android.app.backup.BackupManager.OperationType; import android.app.backup.BackupTransport; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; +import android.app.compat.CompatChanges; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.content.pm.SigningInfo; +import android.os.Build; import android.os.UserHandle; import android.util.Slog; @@ -41,6 +45,7 @@ import com.android.server.backup.transport.TransportClient; import com.google.android.collect.Sets; +import java.util.Objects; import java.util.Set; /** @@ -57,6 +62,15 @@ public class BackupEligibilityRules { private final int mUserId; @OperationType private final int mOperationType; + /** + * When this change is enabled, {@code adb backup} is automatically turned on for apps + * running as debuggable ({@code android:debuggable} set to {@code true}) and unavailable to + * any other apps. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + static final long RESTRICT_ADB_BACKUP = 171032338L; + public static BackupEligibilityRules forBackup(PackageManager packageManager, PackageManagerInternal packageManagerInternal, int userId) { @@ -134,12 +148,53 @@ public class BackupEligibilityRules { * @return boolean indicating whether backup is allowed. */ public boolean isAppBackupAllowed(ApplicationInfo app) { - if (mOperationType == OperationType.MIGRATION && !UserHandle.isCore(app.uid)) { - // Backup / restore of all apps is force allowed during device-to-device migration. - return true; - } + boolean isSystemApp = UserHandle.isCore(app.uid); + boolean allowBackup = (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; + switch (mOperationType) { + case OperationType.MIGRATION: + // Backup / restore of all non-system apps is force allowed during + // device-to-device migration. + return !isSystemApp || allowBackup; + case OperationType.ADB_BACKUP: + String packageName = app.packageName; + if (packageName == null) { + Slog.w(TAG, "Invalid ApplicationInfo object"); + return false; + } + + if (!CompatChanges.isChangeEnabled(RESTRICT_ADB_BACKUP, packageName, + UserHandle.of(mUserId))) { + return allowBackup; + } + + if (PLATFORM_PACKAGE_NAME.equals(packageName)) { + // Always enable adb backup for SystemBackupAgent in "android" package (this is + // done to avoid breaking existing integration tests and might change in the + // future). + return true; + } - return (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; + boolean isPrivileged = (app.flags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; + boolean isDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + if (isSystemApp || isPrivileged) { + try { + return mPackageManager.getProperty(PackageManager.PROPERTY_ALLOW_ADB_BACKUP, + packageName).getBoolean(); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Failed to read allowAdbBackup property for + " + + packageName); + return false; + } + } else { + // All other apps can use adb backup only when running in debuggable mode. + return isDebuggable; + } + case OperationType.BACKUP: + return allowBackup; + default: + Slog.w(TAG, "Unknown operation type:" + mOperationType); + return false; + } } /** diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 6989e320f465..53bfcec11a60 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -949,9 +949,6 @@ public abstract class PackageManagerInternal { /** Returns whether or not permissions need to be upgraded for the given user */ public abstract boolean isPermissionUpgradeNeeded(@UserIdInt int userId); - /** Sets the enforcement of reading external storage */ - public abstract void setReadExternalStorageEnforced(boolean enforced); - /** * Allows the integrity component to respond to the * {@link Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION package verification diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index f0677a23765c..ee9d49244d7e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -169,7 +169,6 @@ import android.util.ArraySet; import android.util.LocalLog; import android.util.Log; import android.util.Pair; -import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.Xml; @@ -1973,7 +1972,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void registerNetdEventCallback() { final IIpConnectivityMetrics ipConnectivityMetrics = mDeps.getIpConnectivityMetrics(); if (ipConnectivityMetrics == null) { - Slog.wtf(TAG, "Missing IIpConnectivityMetrics"); + Log.wtf(TAG, "Missing IIpConnectivityMetrics"); return; } @@ -2439,7 +2438,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (VDBG || DDBG) log("Setting MTU size: " + iface + ", " + mtu); mNetd.interfaceSetMtu(iface, mtu); } catch (RemoteException | ServiceSpecificException e) { - Slog.e(TAG, "exception in interfaceSetMtu()" + e); + loge("exception in interfaceSetMtu()" + e); } } @@ -2461,7 +2460,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (tcpBufferSizes.equals(mCurrentTcpBufferSizes)) return; try { - if (VDBG || DDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes); + if (VDBG || DDBG) log("Setting tx/rx TCP buffers to " + tcpBufferSizes); String rmemValues = String.join(" ", values[0], values[1], values[2]); String wmemValues = String.join(" ", values[3], values[4], values[5]); @@ -2762,7 +2761,7 @@ public class ConnectivityService extends IConnectivityManager.Stub case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: { NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj; if (networkCapabilities.hasConnectivityManagedCapability()) { - Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability."); + Log.wtf(TAG, "BUG: " + nai + " has CS-managed capability."); } if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { // Make sure the original object is not mutated. NetworkAgent normally @@ -3067,7 +3066,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Legacy version of notifyNetworkTestedWithExtras. // Would only be called if the system has a NetworkStack module older than the // framework, which does not happen in practice. - Slog.wtf(TAG, "Deprecated notifyNetworkTested called: no action taken"); + Log.wtf(TAG, "Deprecated notifyNetworkTested called: no action taken"); } @Override @@ -3544,7 +3543,7 @@ public class ConnectivityService extends IConnectivityManager.Stub numRequests = nai.numForegroundNetworkRequests(); break; default: - Slog.wtf(TAG, "Invalid reason. Cannot happen."); + Log.wtf(TAG, "Invalid reason. Cannot happen."); return true; } @@ -3706,7 +3705,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mUidToNetworkRequestCount) { final int requests = mUidToNetworkRequestCount.get(nri.mUid, 0); if (requests < 1) { - Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid); + Log.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid); } else if (requests == 1) { mUidToNetworkRequestCount.removeAt(mUidToNetworkRequestCount.indexOfKey(nri.mUid)); } else { @@ -3751,7 +3750,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (!nai.networkAgentConfig.explicitlySelected) { - Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network"); + Log.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network"); } if (accept != nai.networkAgentConfig.acceptUnvalidated) { @@ -4021,7 +4020,7 @@ public class ConnectivityService extends IConnectivityManager.Stub highPriority = nai.networkAgentConfig.explicitlySelected; break; default: - Slog.wtf(TAG, "Unknown notification type " + type); + Log.wtf(TAG, "Unknown notification type " + type); return; } @@ -4343,7 +4342,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (this) { if (!mNetTransitionWakeLock.isHeld()) { mWakelockLogs.log(String.format("RELEASE: already released (%s)", event)); - Slog.w(TAG, "expected Net Transition WakeLock to be held"); + Log.w(TAG, "expected Net Transition WakeLock to be held"); return; } mNetTransitionWakeLock.release(); @@ -4515,7 +4514,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void onChange(boolean selfChange) { - Slog.wtf(TAG, "Should never be reached."); + Log.wtf(TAG, "Should never be reached."); } @Override @@ -4530,15 +4529,19 @@ public class ConnectivityService extends IConnectivityManager.Stub } private static void log(String s) { - Slog.d(TAG, s); + Log.d(TAG, s); + } + + private static void logw(String s) { + Log.w(TAG, s); } private static void loge(String s) { - Slog.e(TAG, s); + Log.e(TAG, s); } private static void loge(String s, Throwable t) { - Slog.e(TAG, s, t); + Log.e(TAG, s, t); } /** @@ -4825,7 +4828,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public boolean updateLockdownVpn() { if (Binder.getCallingUid() != Process.SYSTEM_UID) { - Slog.w(TAG, "Lockdown VPN only available to AID_SYSTEM"); + logw("Lockdown VPN only available to AID_SYSTEM"); return false; } @@ -4835,21 +4838,21 @@ public class ConnectivityService extends IConnectivityManager.Stub if (mLockdownEnabled) { byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN); if (profileTag == null) { - Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore"); + loge("Lockdown VPN configured but cannot be read from keystore"); return false; } String profileName = new String(profileTag); final VpnProfile profile = VpnProfile.decode( profileName, mKeyStore.get(Credentials.VPN + profileName)); if (profile == null) { - Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName); + loge("Lockdown VPN configured invalid profile " + profileName); setLockdownTracker(null); return true; } int user = UserHandle.getUserId(Binder.getCallingUid()); Vpn vpn = mVpns.get(user); if (vpn == null) { - Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown"); + logw("VPN for user " + user + " not ready yet. Skipping lockdown"); return false; } setLockdownTracker(new LockdownVpnTracker(mContext, this, mHandler, vpn, profile)); @@ -4909,7 +4912,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (vpn == null) { // Shouldn't happen as all code paths that point here should have checked the Vpn // exists already. - Slog.wtf(TAG, "User " + userId + " has no Vpn configuration"); + Log.wtf(TAG, "User " + userId + " has no Vpn configuration"); return false; } @@ -4925,7 +4928,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mVpns) { Vpn vpn = mVpns.get(userId); if (vpn == null) { - Slog.w(TAG, "User " + userId + " has no Vpn configuration"); + logw("User " + userId + " has no Vpn configuration"); return false; } return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore); @@ -4946,7 +4949,7 @@ public class ConnectivityService extends IConnectivityManager.Stub Vpn vpn = mVpns.get(userId); if (vpn == null) { - Slog.w(TAG, "User " + userId + " has no Vpn configuration"); + logw("User " + userId + " has no Vpn configuration"); return false; } if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist, mKeyStore)) { @@ -4968,7 +4971,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mVpns) { Vpn vpn = mVpns.get(userId); if (vpn == null) { - Slog.w(TAG, "User " + userId + " has no Vpn configuration"); + logw("User " + userId + " has no Vpn configuration"); return null; } return vpn.getAlwaysOnPackage(); @@ -4983,7 +4986,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mVpns) { Vpn vpn = mVpns.get(userId); if (vpn == null) { - Slog.w(TAG, "User " + userId + " has no Vpn configuration"); + logw("User " + userId + " has no Vpn configuration"); return false; } return vpn.getLockdown(); @@ -4998,7 +5001,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mVpns) { Vpn vpn = mVpns.get(userId); if (vpn == null) { - Slog.w(TAG, "User " + userId + " has no Vpn configuration"); + logw("User " + userId + " has no Vpn configuration"); return null; } return vpn.getLockdownAllowlist(); @@ -5183,7 +5186,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onPackageReplaced(String packageName, int uid) { if (TextUtils.isEmpty(packageName) || uid < 0) { - Slog.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid); + Log.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid); return; } final int userId = UserHandle.getUserId(uid); @@ -5194,7 +5197,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Legacy always-on VPN won't be affected since the package name is not set. if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) { - Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user " + log("Restarting always-on VPN package " + packageName + " for user " + userId); vpn.startAlwaysOnVpn(mKeyStore); } @@ -5203,7 +5206,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onPackageRemoved(String packageName, int uid, boolean isReplacing) { if (TextUtils.isEmpty(packageName) || uid < 0) { - Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid); + Log.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid); return; } @@ -5215,7 +5218,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Legacy always-on VPN won't be affected since the package name is not set. if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) { - Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user " + log("Removing always-on VPN package " + packageName + " for user " + userId); vpn.setAlwaysOnPackage(null, false, null, mKeyStore); } @@ -5831,7 +5834,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Avoid creating duplicates. even if an app makes a direct AIDL call. // This will never happen if an app calls ConnectivityManager#registerNetworkProvider, // as that will throw if a duplicate provider is registered. - Slog.e(TAG, "Attempt to register existing NetworkProviderInfo " + loge("Attempt to register existing NetworkProviderInfo " + mNetworkProviderInfos.get(npi.messenger).name); return; } @@ -6441,7 +6444,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // stop being matched by the updated agent. String diff = nai.networkCapabilities.describeImmutableDifferences(nc); if (!TextUtils.isEmpty(diff)) { - Slog.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff); + Log.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff); } } @@ -7001,7 +7004,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } newSatisfier.unlingerRequest(nri.request); if (!newSatisfier.addRequest(nri.request)) { - Slog.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has " + Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has " + nri.request); } } else { @@ -7349,7 +7352,7 @@ public class ConnectivityService extends IConnectivityManager.Stub networkAgent.everConnected = true; if (networkAgent.linkProperties == null) { - Slog.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties"); + Log.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties"); } // NetworkCapabilities need to be set before sending the private DNS config to @@ -8197,8 +8200,10 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkRequestInfo nri = cbInfo.mRequestInfo; - if (uid != nri.mUid) { - if (VDBG) loge("Different uid than registrant attempting to unregister cb"); + // Caller's UID must either be the registrants (if they are unregistering) or the System's + // (if the Binder died) + if (uid != nri.mUid && uid != Process.SYSTEM_UID) { + if (DBG) loge("Uid(" + uid + ") not registrant's (" + nri.mUid + ") or System's"); return; } diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 1c99465dfebf..821a967b2b3d 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -24,12 +24,14 @@ import static android.net.INetd.FIREWALL_ALLOWLIST; import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; import static android.net.INetd.FIREWALL_CHAIN_NONE; import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED; import static android.net.INetd.FIREWALL_CHAIN_STANDBY; import static android.net.INetd.FIREWALL_DENYLIST; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkStats.SET_DEFAULT; @@ -88,7 +90,6 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; @@ -122,7 +123,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { * Helper class that encapsulates NetworkManagementService dependencies and makes them * easier to mock in unit tests. */ - static class SystemServices { + static class Dependencies { public IBinder getService(String name) { return ServiceManager.getService(name); } @@ -132,6 +133,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub { public INetd getNetd() { return NetdService.get(); } + + public int getCallingUid() { + return Binder.getCallingUid(); + } } private static final String TAG = "NetworkManagement"; @@ -157,7 +162,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { private final Handler mDaemonHandler; - private final SystemServices mServices; + private final Dependencies mDeps; private INetd mNetdService; @@ -215,6 +220,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub { */ @GuardedBy("mRulesLock") private SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray(); + /** + * Contains the per-UID firewall rules that are used when Restricted Networking Mode is enabled. + */ + @GuardedBy("mRulesLock") + private SparseIntArray mUidFirewallRestrictedRules = new SparseIntArray(); /** Set of states for the child firewall chains. True if the chain is active. */ @GuardedBy("mRulesLock") final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); @@ -254,33 +264,32 @@ public class NetworkManagementService extends INetworkManagementService.Stub { * @param context Binder context for this service */ private NetworkManagementService( - Context context, SystemServices services) { + Context context, Dependencies deps) { mContext = context; - mServices = services; + mDeps = deps; mDaemonHandler = new Handler(FgThread.get().getLooper()); mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener(); - mServices.registerLocalService(new LocalService()); + mDeps.registerLocalService(new LocalService()); synchronized (mTetheringStatsProviders) { mTetheringStatsProviders.put(new NetdTetheringStatsProvider(), "netd"); } } - @VisibleForTesting - NetworkManagementService() { + private NetworkManagementService() { mContext = null; mDaemonHandler = null; - mServices = null; + mDeps = null; mNetdUnsolicitedEventListener = null; } - static NetworkManagementService create(Context context, SystemServices services) + static NetworkManagementService create(Context context, Dependencies deps) throws InterruptedException { final NetworkManagementService service = - new NetworkManagementService(context, services); + new NetworkManagementService(context, deps); if (DBG) Slog.d(TAG, "Creating NetworkManagementService"); if (DBG) Slog.d(TAG, "Connecting native netd service"); service.connectNativeNetdService(); @@ -289,7 +298,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } public static NetworkManagementService create(Context context) throws InterruptedException { - return create(context, new SystemServices()); + return create(context, new Dependencies()); } public void systemReady() { @@ -310,7 +319,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return mBatteryStats; } mBatteryStats = - IBatteryStats.Stub.asInterface(mServices.getService(BatteryStats.SERVICE_NAME)); + IBatteryStats.Stub.asInterface(mDeps.getService(BatteryStats.SERVICE_NAME)); return mBatteryStats; } } @@ -511,7 +520,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } private void connectNativeNetdService() { - mNetdService = mServices.getNetd(); + mNetdService = mDeps.getNetd(); try { mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener); if (DBG) Slog.d(TAG, "Register unsolicited event listener"); @@ -602,9 +611,15 @@ public class NetworkManagementService extends INetworkManagementService.Stub { syncFirewallChainLocked(FIREWALL_CHAIN_STANDBY, "standby "); syncFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, "dozable "); syncFirewallChainLocked(FIREWALL_CHAIN_POWERSAVE, "powersave "); + syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted "); + + final int[] chains = { + FIREWALL_CHAIN_STANDBY, + FIREWALL_CHAIN_DOZABLE, + FIREWALL_CHAIN_POWERSAVE, + FIREWALL_CHAIN_RESTRICTED + }; - final int[] chains = - {FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE}; for (int chain : chains) { if (getFirewallChainState(chain)) { setFirewallChainEnabled(chain, true); @@ -1437,7 +1452,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @Override public void setUidCleartextNetworkPolicy(int uid, int policy) { - if (Binder.getCallingUid() != uid) { + if (mDeps.getCallingUid() != uid) { NetworkStack.checkNetworkStackPermission(mContext); } @@ -1695,6 +1710,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return FIREWALL_CHAIN_NAME_DOZABLE; case FIREWALL_CHAIN_POWERSAVE: return FIREWALL_CHAIN_NAME_POWERSAVE; + case FIREWALL_CHAIN_RESTRICTED: + return FIREWALL_CHAIN_NAME_RESTRICTED; default: throw new IllegalArgumentException("Bad child chain: " + chain); } @@ -1708,6 +1725,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return FIREWALL_ALLOWLIST; case FIREWALL_CHAIN_POWERSAVE: return FIREWALL_ALLOWLIST; + case FIREWALL_CHAIN_RESTRICTED: + return FIREWALL_ALLOWLIST; default: return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST; } @@ -1752,6 +1771,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { case FIREWALL_CHAIN_POWERSAVE: mNetdService.firewallReplaceUidChain("fw_powersave", true, uids); break; + case FIREWALL_CHAIN_RESTRICTED: + mNetdService.firewallReplaceUidChain("fw_restricted", true, uids); + break; case FIREWALL_CHAIN_NONE: default: Slog.d(TAG, "setFirewallUidRules() called on invalid chain: " + chain); @@ -1836,6 +1858,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return mUidFirewallDozableRules; case FIREWALL_CHAIN_POWERSAVE: return mUidFirewallPowerSaveRules; + case FIREWALL_CHAIN_RESTRICTED: + return mUidFirewallRestrictedRules; case FIREWALL_CHAIN_NONE: return mUidFirewallRules; default: @@ -1851,8 +1875,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return rule; } - private static void enforceSystemUid() { - final int uid = Binder.getCallingUid(); + private void enforceSystemUid() { + final int uid = mDeps.getCallingUid(); if (uid != Process.SYSTEM_UID) { throw new SecurityException("Only available to AID_SYSTEM"); } @@ -1910,17 +1934,22 @@ public class NetworkManagementService extends INetworkManagementService.Stub { synchronized (mRulesLock) { dumpUidFirewallRule(pw, "", mUidFirewallRules); - pw.print("UID firewall standby chain enabled: "); pw.println( - getFirewallChainState(FIREWALL_CHAIN_STANDBY)); + pw.print("UID firewall standby chain enabled: "); + pw.println(getFirewallChainState(FIREWALL_CHAIN_STANDBY)); dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_STANDBY, mUidFirewallStandbyRules); - pw.print("UID firewall dozable chain enabled: "); pw.println( - getFirewallChainState(FIREWALL_CHAIN_DOZABLE)); + pw.print("UID firewall dozable chain enabled: "); + pw.println(getFirewallChainState(FIREWALL_CHAIN_DOZABLE)); dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_DOZABLE, mUidFirewallDozableRules); - pw.println("UID firewall powersave chain enabled: " + - getFirewallChainState(FIREWALL_CHAIN_POWERSAVE)); + pw.print("UID firewall powersave chain enabled: "); + pw.println(getFirewallChainState(FIREWALL_CHAIN_POWERSAVE)); dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_POWERSAVE, mUidFirewallPowerSaveRules); + + pw.print("UID firewall restricted mode chain enabled: "); + pw.println(getFirewallChainState(FIREWALL_CHAIN_RESTRICTED)); + dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_RESTRICTED, + mUidFirewallRestrictedRules); } synchronized (mIdleTimerLock) { @@ -2071,6 +2100,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub { if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of power saver mode"); return true; } + if (getFirewallChainState(FIREWALL_CHAIN_RESTRICTED) + && mUidFirewallRestrictedRules.get(uid) != FIREWALL_RULE_ALLOW) { + if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of restricted mode"); + return true; + } if (mUidRejectOnMetered.get(uid)) { if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data" + " in the background"); @@ -2096,60 +2130,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } } - @VisibleForTesting - class LocalService extends NetworkManagementInternal { + private class LocalService extends NetworkManagementInternal { @Override public boolean isNetworkRestrictedForUid(int uid) { return isNetworkRestrictedInternal(uid); } } - - @VisibleForTesting - Injector getInjector() { - return new Injector(); - } - - @VisibleForTesting - class Injector { - void setDataSaverMode(boolean dataSaverMode) { - mDataSaverMode = dataSaverMode; - } - - void setFirewallChainState(int chain, boolean state) { - NetworkManagementService.this.setFirewallChainState(chain, state); - } - - void setFirewallRule(int chain, int uid, int rule) { - synchronized (mRulesLock) { - getUidFirewallRulesLR(chain).put(uid, rule); - } - } - - void setUidOnMeteredNetworkList(boolean denylist, int uid, boolean enable) { - synchronized (mRulesLock) { - if (denylist) { - mUidRejectOnMetered.put(uid, enable); - } else { - mUidAllowOnMetered.put(uid, enable); - } - } - } - - void reset() { - synchronized (mRulesLock) { - setDataSaverMode(false); - final int[] chains = { - FIREWALL_CHAIN_DOZABLE, - FIREWALL_CHAIN_STANDBY, - FIREWALL_CHAIN_POWERSAVE - }; - for (int chain : chains) { - setFirewallChainState(chain, false); - getUidFirewallRulesLR(chain).clear(); - } - mUidAllowOnMetered.clear(); - mUidRejectOnMetered.clear(); - } - } - } } diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index 3443918df6ab..fc3a7c855466 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -59,7 +59,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.util.DumpUtils; -import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -290,7 +290,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { String useOpenWifiPackage = Global.getString(mContext.getContentResolver(), Global.USE_OPEN_WIFI_PACKAGE); if (!TextUtils.isEmpty(useOpenWifiPackage)) { - LocalServices.getService(PermissionManagerServiceInternal.class) + LocalServices.getService(LegacyPermissionManagerInternal.class) .grantDefaultPermissionsToDefaultUseOpenWifiApp(useOpenWifiPackage, userId); } @@ -302,7 +302,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { false /*notifyForDescendants*/, mUseOpenWifiPackageObserver); // Set a callback for the package manager to query the use open wifi app. - LocalServices.getService(PermissionManagerServiceInternal.class) + LocalServices.getService(LegacyPermissionManagerInternal.class) .setUseOpenWifiAppPackagesProvider((userId) -> { String useOpenWifiPackage = Global.getString(mContext.getContentResolver(), Global.USE_OPEN_WIFI_PACKAGE); diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 9bf63cbbb25e..99a1d86d244e 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -52,9 +52,7 @@ import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileNotFoundException; @@ -149,6 +147,7 @@ public class PackageWatchdog { private static final String ATTR_DURATION = "duration"; private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration"; private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check"; + private static final String ATTR_MITIGATION_CALLS = "mitigation-calls"; @GuardedBy("PackageWatchdog.class") private static PackageWatchdog sPackageWatchdog; @@ -779,7 +778,6 @@ public class PackageWatchdog { @GuardedBy("mLock") private Set<String> getPackagesPendingHealthChecksLocked() { - Slog.d(TAG, "Getting all observed packages pending health checks"); Set<String> packages = new ArraySet<>(); Iterator<ObserverInternal> oit = mAllObservers.values().iterator(); while (oit.hasNext()) { @@ -828,7 +826,6 @@ public class PackageWatchdog { Slog.i(TAG, "Cancelling state sync, nothing to sync"); mUptimeAtLastStateSync = 0; } else { - Slog.i(TAG, "Scheduling next state sync in " + durationMs + "ms"); mUptimeAtLastStateSync = mSystemClock.uptimeMillis(); mShortTaskHandler.postDelayed(mSyncStateWithScheduledReason, durationMs); } @@ -869,7 +866,6 @@ public class PackageWatchdog { return; } - Slog.i(TAG, "Removing " + elapsedMs + "ms from all packages on all observers"); Iterator<ObserverInternal> it = mAllObservers.values().iterator(); while (it.hasNext()) { ObserverInternal observer = it.next(); @@ -1067,6 +1063,33 @@ public class PackageWatchdog { } } + /** Convert a {@code LongArrayQueue} to a String of comma-separated values. */ + public static String longArrayQueueToString(LongArrayQueue queue) { + if (queue.size() > 0) { + StringBuilder sb = new StringBuilder(); + sb.append(queue.get(0)); + for (int i = 1; i < queue.size(); i++) { + sb.append(","); + sb.append(queue.get(i)); + } + return sb.toString(); + } + return ""; + } + + /** Parse a comma-separated String of longs into a LongArrayQueue. */ + public static LongArrayQueue parseLongArrayQueue(String commaSeparatedValues) { + LongArrayQueue result = new LongArrayQueue(); + if (!TextUtils.isEmpty(commaSeparatedValues)) { + String[] values = commaSeparatedValues.split(","); + for (String value : values) { + result.addLast(Long.parseLong(value)); + } + } + return result; + } + + /** Dump status of every observer in mAllObservers. */ public void dump(IndentingPrintWriter pw) { pw.println("Package Watchdog status"); @@ -1240,16 +1263,7 @@ public class PackageWatchdog { while (XmlUtils.nextElementWithin(parser, innerDepth)) { if (TAG_PACKAGE.equals(parser.getName())) { try { - String packageName = parser.getAttributeValue( - null, ATTR_NAME); - long duration = parser.getAttributeLong( - null, ATTR_DURATION); - long healthCheckDuration = parser.getAttributeLong( - null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION); - boolean hasPassedHealthCheck = parser.getAttributeBoolean( - null, ATTR_PASSED_HEALTH_CHECK, false); - MonitoredPackage pkg = watchdog.newMonitoredPackage(packageName, - duration, healthCheckDuration, hasPassedHealthCheck); + MonitoredPackage pkg = watchdog.parseMonitoredPackage(parser); if (pkg != null) { packages.add(pkg); } @@ -1305,16 +1319,31 @@ public class PackageWatchdog { MonitoredPackage newMonitoredPackage( String name, long durationMs, boolean hasPassedHealthCheck) { - return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck); + return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck, + new LongArrayQueue()); } MonitoredPackage newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs, - boolean hasPassedHealthCheck) { + boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls) { VersionedPackage pkg = getVersionedPackage(name); if (pkg == null) { return null; } - return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs, hasPassedHealthCheck); + return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs, + hasPassedHealthCheck, mitigationCalls); + } + + MonitoredPackage parseMonitoredPackage(TypedXmlPullParser parser) + throws XmlPullParserException { + String packageName = parser.getAttributeValue(null, ATTR_NAME); + long duration = parser.getAttributeLong(null, ATTR_DURATION); + long healthCheckDuration = parser.getAttributeLong(null, + ATTR_EXPLICIT_HEALTH_CHECK_DURATION); + boolean hasPassedHealthCheck = parser.getAttributeBoolean(null, ATTR_PASSED_HEALTH_CHECK); + LongArrayQueue mitigationCalls = parseLongArrayQueue( + parser.getAttributeValue(null, ATTR_MITIGATION_CALLS)); + return newMonitoredPackage(packageName, + duration, healthCheckDuration, hasPassedHealthCheck, mitigationCalls); } /** @@ -1332,7 +1361,7 @@ public class PackageWatchdog { // Times when an observer was called to mitigate this package's failure. Sorted in // ascending order. @GuardedBy("mLock") - private final LongArrayQueue mMitigationCalls = new LongArrayQueue(); + private final LongArrayQueue mMitigationCalls; // One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after // methods that could change the health check state: handleElapsedTimeLocked and // tryPassHealthCheckLocked @@ -1353,12 +1382,14 @@ public class PackageWatchdog { @GuardedBy("mLock") private long mHealthCheckDurationMs = Long.MAX_VALUE; - private MonitoredPackage(VersionedPackage pkg, long durationMs, - long healthCheckDurationMs, boolean hasPassedHealthCheck) { + MonitoredPackage(VersionedPackage pkg, long durationMs, + long healthCheckDurationMs, boolean hasPassedHealthCheck, + LongArrayQueue mitigationCalls) { mPackage = pkg; mDurationMs = durationMs; mHealthCheckDurationMs = healthCheckDurationMs; mHasPassedHealthCheck = hasPassedHealthCheck; + mMitigationCalls = mitigationCalls; updateHealthCheckStateLocked(); } @@ -1370,6 +1401,8 @@ public class PackageWatchdog { out.attributeLong(null, ATTR_DURATION, mDurationMs); out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs); out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck); + LongArrayQueue normalizedCalls = normalizeMitigationCalls(); + out.attribute(null, ATTR_MITIGATION_CALLS, longArrayQueueToString(normalizedCalls)); out.endTag(null, TAG_PACKAGE); } @@ -1423,6 +1456,23 @@ public class PackageWatchdog { } /** + * Before writing to disk, make the mitigation call timestamps relative to the current + * system uptime. This is because they need to be relative to the uptime which will reset + * at the next boot. + * + * @return a LongArrayQueue of the mitigation calls relative to the current system uptime. + */ + @GuardedBy("mLock") + public LongArrayQueue normalizeMitigationCalls() { + LongArrayQueue normalized = new LongArrayQueue(); + final long now = mSystemClock.uptimeMillis(); + for (int i = 0; i < mMitigationCalls.size(); i++) { + normalized.addLast(mMitigationCalls.get(i) - now); + } + return normalized; + } + + /** * Sets the initial health check duration. * * @return the new health check state @@ -1582,6 +1632,16 @@ public class PackageWatchdog { private long toPositive(long value) { return value > 0 ? value : Long.MAX_VALUE; } + + /** Compares the equality of this object with another {@link MonitoredPackage}. */ + @VisibleForTesting + boolean isEqualTo(MonitoredPackage pkg) { + return (getName().equals(pkg.getName())) + && mDurationMs == pkg.mDurationMs + && mHasPassedHealthCheck == pkg.mHasPassedHealthCheck + && mHealthCheckDurationMs == pkg.mHealthCheckDurationMs + && (mMitigationCalls.toString()).equals(pkg.mMitigationCalls.toString()); + } } /** diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index d30e9fb002e0..51e2b12bcee4 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -158,7 +158,7 @@ public final class SensorPrivacyService extends SystemService { XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY); parser.next(); String tagName = parser.getName(); - enabled = Boolean.valueOf(parser.getAttributeValue(null, XML_ATTRIBUTE_ENABLED)); + enabled = parser.getAttributeBoolean(null, XML_ATTRIBUTE_ENABLED, false); } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Caught an exception reading the state from storage: ", e); // Delete the file to prevent the same error on subsequent calls and assume sensor diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index eb55512b4d83..52eb77d7d47d 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -80,6 +80,7 @@ import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.LocalLog; import android.util.Pair; @@ -101,10 +102,13 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; /** * Since phone process can be restarted, this class provides a centralized place @@ -144,14 +148,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int callerUid; int callerPid; - long events; + Set<Integer> eventList; int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; - boolean matchPhoneStateListenerEvent(long events) { - return (callback != null) && ((events & this.events) != 0); + boolean matchPhoneStateListenerEvent(int event) { + return (callback != null) && (this.eventList.contains(event)); } boolean matchOnSubscriptionsChangedListener() { @@ -179,7 +183,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + onSubscriptionsChangedListenerCallback + " onOpportunisticSubscriptionsChangedListenererCallback=" + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId - + " phoneId=" + phoneId + " events=" + Long.toHexString(events) + "}"; + + " phoneId=" + phoneId + " events=" + eventList + "}"; } } @@ -310,6 +314,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private List<PhysicalChannelConfig> mPhysicalChannelConfigs; + private boolean mIsDataEnabled = false; + + private int mDataEnabledReason; + /** * Per-phone map of precise data connection state. The key of the map is the pair of transport * type and APN setting. This is the cache to prevent redundant callbacks to the listeners. @@ -319,40 +327,62 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>> mPreciseDataConnectionStates; - // Starting in Q, almost all cellular location requires FINE location enforcement. - // Prior to Q, cellular was available with COARSE location enforcement. Bits in this - // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later. - static final long ENFORCE_LOCATION_PERMISSION_MASK = - PhoneStateListener.LISTEN_CELL_LOCATION - | PhoneStateListener.LISTEN_CELL_INFO - | PhoneStateListener.LISTEN_REGISTRATION_FAILURE - | PhoneStateListener.LISTEN_BARRING_INFO; - - static final long ENFORCE_PHONE_STATE_PERMISSION_MASK = - PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR - | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR - | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST - | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED; - - static final long ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK = - PhoneStateListener.LISTEN_PRECISE_CALL_STATE - | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE - | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES - | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED - | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES - | PhoneStateListener.LISTEN_REGISTRATION_FAILURE - | PhoneStateListener.LISTEN_BARRING_INFO - | PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION; - - static final long READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK = - PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL - | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS; - - static final long READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK = - PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT - | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED - | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED - | PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE; + private static final Set<Integer> REQUIRE_PRECISE_PHONE_STATE_PERMISSION; + static { + REQUIRE_PRECISE_PHONE_STATE_PERMISSION = new HashSet<Integer>(); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_DATA_ENABLED_CHANGED); + } + + private boolean isLocationPermissionRequired(Set<Integer> events) { + return events.contains(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED) + || events.contains(PhoneStateListener.EVENT_CELL_INFO_CHANGED) + || events.contains(PhoneStateListener.EVENT_REGISTRATION_FAILURE) + || events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + } + + private boolean isPhoneStatePermissionRequired(Set<Integer> events) { + return events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED) + || events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED) + || events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED) + || events.contains(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); + } + + private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) { + for (Integer requireEvent : REQUIRE_PRECISE_PHONE_STATE_PERMISSION) { + if (events.contains(requireEvent)) { + return true; + } + } + return false; + } + + private boolean isActiveEmergencySessionPermissionRequired(Set<Integer> events) { + return events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL) + || events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); + } + + private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) { + return events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED) + || events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED) + || events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); + } private static final int MSG_USER_SWITCHED = 1; private static final int MSG_UPDATE_DEFAULT_SUB = 2; @@ -670,7 +700,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callingFeatureId = callingFeatureId; r.callerUid = Binder.getCallingUid(); r.callerPid = Binder.getCallingPid(); - r.events = 0; + r.eventList = new ArraySet<>(); if (DBG) { log("listen oscl: Register r=" + r); } @@ -724,7 +754,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callingFeatureId = callingFeatureId; r.callerUid = Binder.getCallingUid(); r.callerPid = Binder.getCallingPid(); - r.events = 0; + r.eventList = new ArraySet<>(); if (DBG) { log("listen ooscl: Register r=" + r); } @@ -797,331 +827,337 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - @Deprecated - @Override - public void listen(String callingPackage, IPhoneStateListener callback, int events, - boolean notifyNow) { - listenWithFeature(callingPackage, null, callback, events, notifyNow); - } - - @Override - public void listenWithFeature(String callingPackage, String callingFeatureId, - IPhoneStateListener callback, long events, boolean notifyNow) { - listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, callingPackage, - callingFeatureId, callback, events, notifyNow); - } - @Override - public void listenForSubscriber(int subId, String callingPackage, String callingFeatureId, - IPhoneStateListener callback, long events, boolean notifyNow) { - listen(callingPackage, callingFeatureId, callback, events, notifyNow, subId); + public void listenWithEventList(int subId, String callingPackage, String callingFeatureId, + IPhoneStateListener callback, int[] events, boolean notifyNow) { + Set<Integer> eventList = Arrays.stream(events).boxed().collect(Collectors.toSet()); + listen(callingPackage, callingFeatureId, callback, eventList, notifyNow, subId); } private void listen(String callingPackage, @Nullable String callingFeatureId, - IPhoneStateListener callback, long events, boolean notifyNow, int subId) { + IPhoneStateListener callback, Set<Integer> events, boolean notifyNow, int subId) { int callerUserId = UserHandle.getCallingUserId(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid() - + " events=0x" + Long.toHexString(events) + " notifyNow=" + notifyNow + " subId=" - + subId + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId; + + " events=" + events + " notifyNow=" + notifyNow + + " subId=" + subId + " myUserId=" + UserHandle.myUserId() + + " callerUserId=" + callerUserId; mListenLog.log(str); if (VDBG) { log(str); } - if (events != PhoneStateListener.LISTEN_NONE) { - // Checks permission and throws SecurityException for disallowed operations. For pre-M - // apps whose runtime permission has been revoked, we return immediately to skip sending - // events to the app without crashing it. - if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId, - "listen")) { - return; + if (events.isEmpty()) { + if (DBG) { + log("listen: Unregister"); } + events.clear(); + remove(callback.asBinder()); + return; + } - int phoneId = getPhoneIdFromSubId(subId); - synchronized (mRecords) { - // register - IBinder b = callback.asBinder(); - boolean doesLimitApply = - Binder.getCallingUid() != Process.SYSTEM_UID - && Binder.getCallingUid() != Process.PHONE_UID - && Binder.getCallingUid() != Process.myUid(); - Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply); + // Checks permission and throws SecurityException for disallowed operations. For pre-M + // apps whose runtime permission has been revoked, we return immediately to skip sending + // events to the app without crashing it. + if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId, + "listen")) { + return; + } - if (r == null) { - return; - } + int phoneId = getPhoneIdFromSubId(subId); + synchronized (mRecords) { + // register + IBinder b = callback.asBinder(); + boolean doesLimitApply = + Binder.getCallingUid() != Process.SYSTEM_UID + && Binder.getCallingUid() != Process.PHONE_UID + && Binder.getCallingUid() != Process.myUid(); + Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply); - r.context = mContext; - r.callback = callback; - r.callingPackage = callingPackage; - r.callingFeatureId = callingFeatureId; - r.callerUid = Binder.getCallingUid(); - r.callerPid = Binder.getCallingPid(); - // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID, - // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID - if (!SubscriptionManager.isValidSubscriptionId(subId)) { - r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; - } else {//APP specify subID - r.subId = subId; - } - r.phoneId = phoneId; - r.events = events; - if (DBG) { - log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); - } - if (notifyNow && validatePhoneId(phoneId)) { - if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { - try { - if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); - ServiceState rawSs = new ServiceState(mServiceState[phoneId]); - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onServiceStateChanged(rawSs); - } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onServiceStateChanged( - rawSs.createLocationInfoSanitizedCopy(false)); - } else { - r.callback.onServiceStateChanged( - rawSs.createLocationInfoSanitizedCopy(true)); - } - } catch (RemoteException ex) { - remove(r.binder); + if (r == null) { + return; + } + + r.context = mContext; + r.callback = callback; + r.callingPackage = callingPackage; + r.callingFeatureId = callingFeatureId; + r.callerUid = Binder.getCallingUid(); + r.callerPid = Binder.getCallingPid(); + // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID, + // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; + } else {//APP specify subID + r.subId = subId; + } + r.phoneId = phoneId; + r.eventList = events; + if (DBG) { + log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); + } + if (notifyNow && validatePhoneId(phoneId)) { + if (events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)) { + try { + if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); + ServiceState rawSs = new ServiceState(mServiceState[phoneId]); + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(rawSs); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged( + rawSs.createLocationInfoSanitizedCopy(false)); + } else { + r.callback.onServiceStateChanged( + rawSs.createLocationInfoSanitizedCopy(true)); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { - try { - if (mSignalStrength[phoneId] != null) { - int gsmSignalStrength = mSignalStrength[phoneId] - .getGsmSignalStrength(); - r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 - : gsmSignalStrength)); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) { + try { + if (mSignalStrength[phoneId] != null) { + int gsmSignalStrength = mSignalStrength[phoneId] + .getGsmSignalStrength(); + r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 + : gsmSignalStrength)); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { - try { - r.callback.onMessageWaitingIndicatorChanged( - mMessageWaiting[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains( + PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) { + try { + r.callback.onMessageWaitingIndicatorChanged( + mMessageWaiting[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { - try { - r.callback.onCallForwardingIndicatorChanged( - mCallForwarding[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains( + PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) { + try { + r.callback.onCallForwardingIndicatorChanged( + mCallForwarding[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { - try { - if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); - if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) - && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - // null will be translated to empty CellLocation object in client. - r.callback.onCellLocationChanged(mCellIdentity[phoneId]); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (validateEventAndUserLocked( + r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) { + try { + if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + // null will be translated to empty CellLocation object in client. + r.callback.onCellLocationChanged(mCellIdentity[phoneId]); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { - try { - r.callback.onCallStateChanged(mCallState[phoneId], - getCallIncomingNumber(r, phoneId)); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_CALL_STATE_CHANGED)) { + try { + r.callback.onCallStateChanged(mCallState[phoneId], + getCallIncomingNumber(r, phoneId)); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { - try { - r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], + } + if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) { + try { + r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], mDataConnectionNetworkType[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { - try { - r.callback.onDataActivity(mDataActivity[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)) { + try { + r.callback.onDataActivity(mDataActivity[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { - try { - if (mSignalStrength[phoneId] != null) { - r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)) { + try { + if (mSignalStrength[phoneId] != null) { + r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) - != 0) { - updateReportSignalStrengthDecision(r.subId); - try { - if (mSignalStrength[phoneId] != null) { - r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (events.contains( + PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { + updateReportSignalStrengthDecision(r.subId); + try { + if (mSignalStrength[phoneId] != null) { + r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); } + } catch (RemoteException ex) { + remove(r.binder); } - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) { - try { - if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " - + mCellInfo.get(phoneId)); - if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) - && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (validateEventAndUserLocked( + r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) { + try { + if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + + mCellInfo.get(phoneId)); + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) { - try { - r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)) { + try { + r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { - try { - r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId], - mCallPreciseDisconnectCause[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) { + try { + r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId], + mCallPreciseDisconnectCause[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) { - try { - r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId)); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) { + try { + r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId)); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) { - try { - for (PreciseDataConnectionState pdcs - : mPreciseDataConnectionStates.get(phoneId).values()) { - r.callback.onPreciseDataConnectionStateChanged(pdcs); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (events.contains( + PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) { + try { + for (PreciseDataConnectionState pdcs + : mPreciseDataConnectionStates.get(phoneId).values()) { + r.callback.onPreciseDataConnectionStateChanged(pdcs); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) { - try { - r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)) { + try { + r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) !=0) { - try { - r.callback.onVoiceActivationStateChanged( - mVoiceActivationState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) { + try { + r.callback.onVoiceActivationStateChanged( + mVoiceActivationState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) !=0) { - try { - r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)) { + try { + r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) { - try { - r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) { + try { + r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { - try { - if (mTelephonyDisplayInfos[phoneId] != null) { - r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) { + try { + if (mTelephonyDisplayInfos[phoneId] != null) { + r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) { - try { - r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) { + try { + r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) { - try { - r.callback.onPhoneCapabilityChanged(mPhoneCapability); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) { + try { + r.callback.onPhoneCapabilityChanged(mPhoneCapability); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener - .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) { - try { - r.callback.onActiveDataSubIdChanged(mActiveDataSubId); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains( + PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) { + try { + r.callback.onActiveDataSubIdChanged(mActiveDataSubId); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) { - try { - r.callback.onRadioPowerStateChanged(mRadioPowerState); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)) { + try { + r.callback.onRadioPowerStateChanged(mRadioPowerState); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) { - try { - r.callback.onSrvccStateChanged(mSrvccState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)) { + try { + r.callback.onSrvccStateChanged(mSrvccState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) { - try { - r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)) { + try { + r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_BARRING_INFO) != 0) { - BarringInfo barringInfo = mBarringInfo.get(phoneId); - BarringInfo biNoLocation = barringInfo != null - ? barringInfo.createLocationInfoSanitizedCopy() : null; - if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); - try { - r.callback.onBarringInfoChanged( - checkFineLocationAccess(r, Build.VERSION_CODES.BASE) - ? barringInfo : biNoLocation); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED)) { + BarringInfo barringInfo = mBarringInfo.get(phoneId); + BarringInfo biNoLocation = barringInfo != null + ? barringInfo.createLocationInfoSanitizedCopy() : null; + if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); + try { + r.callback.onBarringInfoChanged( + checkFineLocationAccess(r, Build.VERSION_CODES.BASE) + ? barringInfo : biNoLocation); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) != 0) { - try { - r.callback.onPhysicalChannelConfigurationChanged( - mPhysicalChannelConfigs); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains( + PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) { + try { + r.callback.onPhysicalChannelConfigChanged( + mPhysicalChannelConfigs); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if (events.contains( + PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) { + try { + r.callback.onDataEnabledChanged(mIsDataEnabled, mDataEnabledReason); + } catch (RemoteException ex) { + remove(r.binder); } } } - } else { - if(DBG) log("listen: Unregister"); - remove(callback.asBinder()); } } @@ -1133,7 +1169,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // If any of the system clients wants to always listen to signal strength, // we need to set it on. if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) { + PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { telephonyManager.createForSubscriptionId(subscriptionId) .setAlwaysReportSignalStrength(true); return; @@ -1234,7 +1270,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // strength is removed from registry records, we need to check if // the signal strength decision needs to update on its slot. if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) { + PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { updateReportSignalStrengthDecision(r.subId); } return; @@ -1254,8 +1290,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) && - (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { + if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED) + && (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { try { // Ensure the listener has read call log permission; if they do not return // an empty phone number. @@ -1289,9 +1325,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallState[phoneId] = state; mCallIncomingNumber[phoneId] = incomingNumber; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) && - (r.subId == subId) && - (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { + if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED) + && (r.subId == subId) + && (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { try { String incomingNumberOrEmpty = getCallIncomingNumber(r, phoneId); r.callback.onCallStateChanged(state, incomingNumberOrEmpty); @@ -1331,8 +1367,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + state); } - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) && - idMatch(r.subId, subId, phoneId)) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_SERVICE_STATE_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { ServiceState stateToSend; @@ -1393,7 +1430,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if ((activationType == SIM_ACTIVATION_TYPE_VOICE) && r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) + PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r @@ -1404,7 +1441,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } if ((activationType == SIM_ACTIVATION_TYPE_DATA) && r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) + PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r @@ -1443,9 +1480,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " ss=" + signalStrength); } - if ((r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) + if ((r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED) || r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) + PhoneStateListener. + EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { @@ -1458,8 +1497,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mRemoveList.add(r.binder); } } - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTH) && - idMatch(r.subId, subId, phoneId)) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { int gsmSignalStrength = signalStrength.getGsmSignalStrength(); int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength); @@ -1505,8 +1545,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) && - idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCarrierNetworkChange(active); } catch (RemoteException ex) { @@ -1536,9 +1576,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mCellInfo.set(phoneId, cellInfo); for (Record r : mRecords) { - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && - idMatch(r.subId, subId, phoneId) && - (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + if (validateEventAndUserLocked( + r, PhoneStateListener.EVENT_CELL_INFO_CHANGED) + && idMatch(r.subId, subId, phoneId) + && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { @@ -1570,8 +1611,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mMessageWaiting[phoneId] = mwi; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) && - idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onMessageWaitingIndicatorChanged(mwi); } catch (RemoteException ex) { @@ -1597,8 +1638,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mUserMobileDataState[phoneId] = state; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) && - idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onUserMobileDataStateChanged(state); } catch (RemoteException ex) { @@ -1636,7 +1677,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) + PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED) && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) { try { r.callback.onDisplayInfoChanged(telephonyDisplayInfo); @@ -1668,8 +1709,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallForwarding[phoneId] = cfi; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) && - idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallForwardingIndicatorChanged(cfi); } catch (RemoteException ex) { @@ -1696,8 +1737,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mDataActivity[phoneId] = state; for (Record r : mRecords) { // Notify by correct subId. - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_DATA_ACTIVITY) && - idMatch(r.subId, subId, phoneId)) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onDataActivity(state); } catch (RemoteException ex) { @@ -1744,7 +1786,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mLocalLog.log(str); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) + PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { @@ -1769,7 +1811,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (!Objects.equals(oldState, preciseState)) { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) + PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onPreciseDataConnectionStateChanged(preciseState); @@ -1814,9 +1856,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId) && !Objects.equals(cellIdentity, mCellIdentity[phoneId])) { mCellIdentity[phoneId] = cellIdentity; for (Record r : mRecords) { - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && - idMatch(r.subId, subId, phoneId) && - (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + if (validateEventAndUserLocked( + r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED) + && idMatch(r.subId, subId, phoneId) + && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { @@ -1867,7 +1910,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE) + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); @@ -1876,7 +1920,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (notifyCallAttributes && r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) + PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); @@ -1925,7 +1969,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mImsReasonInfo.set(phoneId, imsReasonInfo); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) + PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { @@ -1957,8 +2001,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSrvccState[phoneId] = state; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) && - idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.EVENT_SRVCC_STATE_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { log("notifySrvccStateChanged: mSrvccState=" + state + " r=" + r); @@ -1986,7 +2030,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId); } if ((r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) + PhoneStateListener.EVENT_OEM_HOOK_RAW)) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onOemHookRawEvent(rawData); @@ -2014,7 +2058,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE)) { + PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) { try { r.callback.onPhoneCapabilityChanged(capability); } catch (RemoteException ex) { @@ -2039,7 +2083,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)) { + PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) { try { r.callback.onActiveDataSubIdChanged(activeDataSubId); } catch (RemoteException ex) { @@ -2066,7 +2110,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) + PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onRadioPowerStateChanged(state); @@ -2095,7 +2139,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) + PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); @@ -2127,7 +2171,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { // Send to all listeners regardless of subscription if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL)) { + PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)) { try { r.callback.onOutgoingEmergencyCall(emergencyNumber, subId); } catch (RemoteException ex) { @@ -2151,7 +2195,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { // Send to all listeners regardless of subscription if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)) { + PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS)) { try { r.callback.onOutgoingEmergencySms(emergencyNumber, subId); } catch (RemoteException ex) { @@ -2181,7 +2225,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) + PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); @@ -2212,7 +2256,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_REGISTRATION_FAILURE) + PhoneStateListener.EVENT_REGISTRATION_FAILURE) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onRegistrationFailed( @@ -2255,7 +2299,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_BARRING_INFO) + PhoneStateListener.EVENT_BARRING_INFO_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { @@ -2282,14 +2326,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { * @param subId the subId * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel. */ - public void notifyPhysicalChannelConfigurationForSubscriber( + public void notifyPhysicalChannelConfigForSubscriber( int subId, List<PhysicalChannelConfig> configs) { - if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) { + if (!checkNotifyPermission("notifyPhysicalChannelConfig()")) { return; } if (VDBG) { - log("notifyPhysicalChannelConfiguration: subId=" + subId + " configs=" + configs); + log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs); } synchronized (mRecords) { @@ -2298,15 +2342,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId)); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) + PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { - log("notifyPhysicalChannelConfiguration: " + log("notifyPhysicalChannelConfig: " + "mPhysicalChannelConfigs=" + configs + " r=" + r); } - r.callback.onPhysicalChannelConfigurationChanged(configs); + r.callback.onPhysicalChannelConfigChanged(configs); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -2616,18 +2660,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { == PackageManager.PERMISSION_GRANTED; } - private boolean checkListenerPermission(long events, int subId, String callingPackage, - @Nullable String callingFeatureId, String message) { + private boolean checkListenerPermission(Set<Integer> events, int subId, String callingPackage, + @Nullable String callingFeatureId, String message) { LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder = new LocationAccessPolicy.LocationPermissionQuery.Builder() - .setCallingPackage(callingPackage) - .setMethod(message + " events: " + events) - .setCallingPid(Binder.getCallingPid()) - .setCallingUid(Binder.getCallingUid()); + .setCallingPackage(callingPackage) + .setMethod(message + " events: " + events) + .setCallingPid(Binder.getCallingPid()) + .setCallingUid(Binder.getCallingUid()); boolean shouldCheckLocationPermissions = false; - if ((events & ENFORCE_LOCATION_PERMISSION_MASK) != 0) { + if (isLocationPermissionRequired(events)) { // Everything that requires fine location started in Q. So far... locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q); // If we're enforcing fine starting in Q, we also want to enforce coarse even for @@ -2652,14 +2696,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) { + if (isPhoneStatePermissionRequired(events)) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mContext, subId, callingPackage, callingFeatureId, message)) { isPermissionCheckSuccessful = false; } } - if ((events & ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) { + if (isPrecisePhoneStatePermissionRequired(events)) { // check if calling app has either permission READ_PRECISE_PHONE_STATE // or with carrier privileges try { @@ -2670,21 +2714,20 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK) != 0) { + if (isActiveEmergencySessionPermissionRequired(events)) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null); } - if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { + if ((events.contains(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null); } - if ((events & READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK) != 0) { + if (isPrivilegedPhoneStatePermissionRequired(events)) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); } - return isPermissionCheckSuccessful; } @@ -2692,25 +2735,25 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int size = mRemoveList.size(); if (VDBG) log("handleRemoveListLocked: mRemoveList.size()=" + size); if (size > 0) { - for (IBinder b: mRemoveList) { + for (IBinder b : mRemoveList) { remove(b); } mRemoveList.clear(); } } - private boolean validateEventsAndUserLocked(Record r, int events) { + private boolean validateEventAndUserLocked(Record r, int event) { int foregroundUser; final long callingIdentity = Binder.clearCallingIdentity(); boolean valid = false; try { foregroundUser = ActivityManager.getCurrentUser(); valid = UserHandle.getUserId(r.callerUid) == foregroundUser - && r.matchPhoneStateListenerEvent(events); + && r.matchPhoneStateListenerEvent(event); if (DBG | DBG_LOC) { - log("validateEventsAndUserLocked: valid=" + valid + log("validateEventAndUserLocked: valid=" + valid + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser - + " r.events=" + r.events + " events=" + events); + + " r.eventList=" + r.eventList + " event=" + event); } } finally { Binder.restoreCallingIdentity(callingIdentity); @@ -2822,9 +2865,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } private void checkPossibleMissNotify(Record r, int phoneId) { - long events = r.events; + Set<Integer> events = r.eventList; + + if (events == null || events.isEmpty()) { + log("checkPossibleMissNotify: events = null."); + return; + } - if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { + if ((events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED))) { try { if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" + mServiceState[phoneId]); @@ -2843,8 +2891,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0 - || (events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { + if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED) + || events.contains( + PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { try { if (mSignalStrength[phoneId] != null) { SignalStrength signalStrength = mSignalStrength[phoneId]; @@ -2859,7 +2908,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { + if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) { try { if (mSignalStrength[phoneId] != null) { int gsmSignalStrength = mSignalStrength[phoneId] @@ -2876,7 +2925,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) { + if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) { try { if (DBG_LOC) { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " @@ -2891,7 +2940,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) { + if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId=" @@ -2903,7 +2952,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { + if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onDisplayInfoChanged phoneId=" @@ -2917,7 +2966,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { + if (events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onMessageWaitingIndicatorChanged phoneId=" @@ -2930,7 +2979,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { + if (events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onCallForwardingIndicatorChanged phoneId=" @@ -2943,7 +2992,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { + if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) { try { if (DBG_LOC) { log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = " @@ -2959,7 +3008,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { + if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) { try { if (DBG) { log("checkPossibleMissNotify: onDataConnectionStateChanged(mDataConnectionState" diff --git a/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java b/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java index b9453db8eb0a..725bccf76bb9 100644 --- a/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java +++ b/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java @@ -27,10 +27,10 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; @@ -83,13 +83,13 @@ import java.io.IOException; private static class MySerializer implements XmlSerializerAndParser<AuthenticatorDescription> { @Override - public void writeAsXml(AuthenticatorDescription item, XmlSerializer out) + public void writeAsXml(AuthenticatorDescription item, TypedXmlSerializer out) throws IOException { out.attribute(null, "type", item.type); } @Override - public AuthenticatorDescription createFromXml(XmlPullParser parser) + public AuthenticatorDescription createFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { return AuthenticatorDescription.newKey(parser.getAttributeValue(null, "type")); } diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index d99b195351f1..1f35b88c8cbd 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -71,7 +71,6 @@ import android.util.Xml; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; import com.android.internal.util.dump.DualDumpOutputStream; @@ -79,7 +78,6 @@ import com.android.server.FgThread; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.BufferedReader; import java.io.File; @@ -89,7 +87,6 @@ import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.AbstractMap; @@ -1757,21 +1754,21 @@ public class AdbDebuggingManager { dump.write("user_keys", AdbDebuggingManagerProto.USER_KEYS, FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null)); } catch (IOException e) { - Slog.e(TAG, "Cannot read user keys", e); + Slog.i(TAG, "Cannot read user keys", e); } try { dump.write("system_keys", AdbDebuggingManagerProto.SYSTEM_KEYS, FileUtils.readTextFile(new File("/adb_keys"), 0, null)); } catch (IOException e) { - Slog.e(TAG, "Cannot read system keys", e); + Slog.i(TAG, "Cannot read system keys", e); } try { dump.write("keystore", AdbDebuggingManagerProto.KEYSTORE, FileUtils.readTextFile(getAdbTempKeysFile(), 0, null)); } catch (IOException e) { - Slog.e(TAG, "Cannot read keystore: ", e); + Slog.i(TAG, "Cannot read keystore: ", e); } dump.end(token); @@ -1966,9 +1963,9 @@ public class AdbDebuggingManager { String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY); long connectionTime; try { - connectionTime = Long.valueOf( - parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION)); - } catch (NumberFormatException e) { + connectionTime = parser.getAttributeLong(null, + XML_ATTRIBUTE_LAST_CONNECTION); + } catch (XmlPullParserException e) { Slog.e(TAG, "Caught a NumberFormatException parsing the last connection time: " + e); @@ -2020,9 +2017,9 @@ public class AdbDebuggingManager { String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY); long connectionTime; try { - connectionTime = Long.valueOf( - parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION)); - } catch (NumberFormatException e) { + connectionTime = parser.getAttributeLong(null, + XML_ATTRIBUTE_LAST_CONNECTION); + } catch (XmlPullParserException e) { Slog.e(TAG, "Caught a NumberFormatException parsing the last connection time: " + e); @@ -2150,8 +2147,8 @@ public class AdbDebuggingManager { for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) { serializer.startTag(null, XML_TAG_ADB_KEY); serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey()); - serializer.attribute(null, XML_ATTRIBUTE_LAST_CONNECTION, - String.valueOf(keyEntry.getValue())); + serializer.attributeLong(null, XML_ATTRIBUTE_LAST_CONNECTION, + keyEntry.getValue()); serializer.endTag(null, XML_TAG_ADB_KEY); } for (String bssid : mTrustedNetworks) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index da6e7ff84525..bcd122d33f7d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -139,6 +139,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.Activity; +import android.app.ActivityClient; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManagerInternal; @@ -264,7 +265,6 @@ import android.os.UserManager; import android.os.WorkSource; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; -import android.permission.PermissionManagerInternal.CheckPermissionDelegate; import android.provider.DeviceConfig; import android.provider.Settings; import android.server.ServerProtoEnums; @@ -288,7 +288,6 @@ import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoUtils; -import android.view.Display; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -326,7 +325,6 @@ import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.QuadFunction; -import com.android.internal.util.function.TriFunction; import com.android.server.AlarmManagerInternal; import com.android.server.AttributeCache; import com.android.server.DeviceIdleInternal; @@ -394,7 +392,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; -import java.util.function.BiFunction; public class ActivityManagerService extends IActivityManager.Stub implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { @@ -2859,12 +2856,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public final boolean finishActivity(IBinder token, int resultCode, Intent resultData, int finishTask) { - return mActivityTaskManager.finishActivity(token, resultCode, resultData, finishTask); + return ActivityClient.getInstance().finishActivity(token, resultCode, resultData, + finishTask); } @Override public void setRequestedOrientation(IBinder token, int requestedOrientation) { - mActivityTaskManager.setRequestedOrientation(token, requestedOrientation); + ActivityClient.getInstance().setRequestedOrientation(token, requestedOrientation); } @Override @@ -5711,7 +5709,7 @@ public class ActivityManagerService extends IActivityManager.Stub */ @Override public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) { - return mActivityTaskManager.moveActivityTaskToBack(token, nonRoot); + return ActivityClient.getInstance().moveActivityTaskToBack(token, nonRoot); } @Override @@ -5746,7 +5744,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int getTaskForActivity(IBinder token, boolean onlyRoot) { - return mActivityTaskManager.getTaskForActivity(token, onlyRoot); + return ActivityClient.getInstance().getTaskForActivity(token, onlyRoot); } @Override @@ -6719,7 +6717,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public boolean isTopOfTask(IBinder token) { - return mActivityTaskManager.isTopOfTask(token); + return ActivityClient.getInstance().isTopOfTask(token); } @Override @@ -14485,7 +14483,7 @@ public class ActivityManagerService extends IActivityManager.Stub mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid, app.info.packageName, AppOpsManager.MODE_ERRORED); mAppOpsService.setAppOpsServiceDelegate(null); - getPermissionManagerInternalLocked().setCheckPermissionDelegate(null); + getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation(); mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG, instr.mUiAutomationConnection).sendToTarget(); } @@ -16305,14 +16303,9 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public ActivityPresentationInfo getActivityPresentationInfo(IBinder token) { - int displayId = Display.INVALID_DISPLAY; - try { - displayId = mActivityTaskManager.getDisplayId(token); - } catch (RemoteException e) { - } - - return new ActivityPresentationInfo(mActivityTaskManager.getTaskForActivity(token, - /*onlyRoot=*/ false), displayId, + final ActivityClient ac = ActivityClient.getInstance(); + return new ActivityPresentationInfo(ac.getTaskForActivity(token, + /*onlyRoot=*/ false), ac.getDisplayId(token), mActivityTaskManager.getActivityClassForToken(token)); } @@ -17205,12 +17198,6 @@ public class ActivityManagerService extends IActivityManager.Stub // We allow delegation only to one instrumentation started from the shell synchronized (ActivityManagerService.this) { - // If there is a delegate it should be the same instance for app ops and permissions. - if (mAppOpsService.getAppOpsServiceDelegate() - != getPermissionManagerInternalLocked().getCheckPermissionDelegate()) { - throw new IllegalStateException("Bad shell delegate state"); - } - // If the delegate is already set up for the target UID, nothing to do. if (mAppOpsService.getAppOpsServiceDelegate() != null) { if (!(mAppOpsService.getAppOpsServiceDelegate() instanceof ShellDelegate)) { @@ -17239,10 +17226,14 @@ public class ActivityManagerService extends IActivityManager.Stub } // Hook them up... - final ShellDelegate shellDelegate = new ShellDelegate( - instr.mTargetInfo.packageName, delegateUid, permissions); + final ShellDelegate shellDelegate = new ShellDelegate(delegateUid, + permissions); mAppOpsService.setAppOpsServiceDelegate(shellDelegate); - getPermissionManagerInternalLocked().setCheckPermissionDelegate(shellDelegate); + final String packageName = instr.mTargetInfo.packageName; + final List<String> permissionNames = permissions != null ? + Arrays.asList(permissions) : null; + getPermissionManagerInternalLocked().startShellPermissionIdentityDelegation( + delegateUid, packageName, permissionNames); return; } } @@ -17256,17 +17247,15 @@ public class ActivityManagerService extends IActivityManager.Stub } synchronized (ActivityManagerService.this) { mAppOpsService.setAppOpsServiceDelegate(null); - getPermissionManagerInternalLocked().setCheckPermissionDelegate(null); + getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation(); } } - private class ShellDelegate implements CheckOpsDelegate, CheckPermissionDelegate { - private final String mTargetPackageName; + private class ShellDelegate implements CheckOpsDelegate { private final int mTargetUid; private @Nullable String[] mPermissions; - ShellDelegate(String targetPackageName, int targetUid, @Nullable String[] permissions) { - mTargetPackageName = targetPackageName; + ShellDelegate(int targetUid, @Nullable String[] permissions) { mTargetUid = targetUid; mPermissions = permissions; } @@ -17329,34 +17318,6 @@ public class ActivityManagerService extends IActivityManager.Stub message, shouldCollectMessage); } - @Override - public int checkPermission(String permName, String pkgName, int userId, - TriFunction<String, String, Integer, Integer> superImpl) { - if (mTargetPackageName.equals(pkgName) && isTargetPermission(permName)) { - final long identity = Binder.clearCallingIdentity(); - try { - return superImpl.apply(permName, "com.android.shell", userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - return superImpl.apply(permName, pkgName, userId); - } - - @Override - public int checkUidPermission(String permName, int uid, - BiFunction<String, Integer, Integer> superImpl) { - if (uid == mTargetUid && isTargetPermission(permName)) { - final long identity = Binder.clearCallingIdentity(); - try { - return superImpl.apply(permName, Process.SHELL_UID); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - return superImpl.apply(permName, uid); - } - private boolean isTargetOp(int code) { // null permissions means all ops are targeted if (mPermissions == null) { diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index fdc0f5949da8..ca20224ce315 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -353,7 +353,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mUidsToRemove.clear(); mCurrentFuture = null; mUseLatestStates = true; - if ((updateFlags & UPDATE_ALL) != 0) { + if (updateFlags == UPDATE_ALL) { cancelSyncDueToBatteryLevelChangeLocked(); } if ((updateFlags & UPDATE_CPU) != 0) { @@ -533,7 +533,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff); } - if ((updateFlags & UPDATE_ALL) != 0) { + if (updateFlags == UPDATE_ALL) { mStats.updateKernelWakelocksLocked(elapsedRealtimeUs); mStats.updateKernelMemoryBandwidthLocked(elapsedRealtimeUs); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 46e16bcbb1b6..3b6f0ac42ed2 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -25,6 +25,7 @@ import android.hardware.power.stats.EnergyConsumerId; import android.hardware.power.stats.EnergyConsumerResult; import android.os.BatteryStats; import android.os.BatteryStatsInternal; +import android.os.BatteryUsageStats; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; @@ -57,6 +58,7 @@ import android.util.Slog; import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.os.BatteryUsageStatsProvider; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.RailStats; @@ -105,6 +107,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider; private final Context mContext; private final BatteryExternalStatsWorker mWorker; + private final BatteryUsageStatsProvider mBatteryUsageStatsProvider; private native void getLowPowerStats(RpmStats rpmStats); private native int getPlatformLowPowerStats(ByteBuffer outBuffer); @@ -258,6 +261,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); mStats.setPowerProfileLocked(new PowerProfile(context)); + mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats); } public void publish() { @@ -550,6 +554,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub // Public interface... + /** + * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem + * and per-UID basis. + */ + public BatteryUsageStats getBatteryUsageStats() { + mContext.enforceCallingPermission( + android.Manifest.permission.BATTERY_STATS, null); + return mBatteryUsageStatsProvider.getBatteryUsageStats(); + } + public byte[] getStatistics() { mContext.enforceCallingPermission( android.Manifest.permission.BATTERY_STATS, null); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 89150aee44ad..4d971a51e885 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -545,7 +545,6 @@ public class AudioService extends IAudioService.Stub AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, AudioSystem.DEVICE_OUT_HDMI_ARC, - AudioSystem.DEVICE_OUT_SPDIF, AudioSystem.DEVICE_OUT_AUX_LINE)); // Devices for which the volume is always max, no volume panel Set<Integer> mFullVolumeDevices = new HashSet<>(); diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 0194259d6289..75e1938656e3 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -30,6 +30,7 @@ import android.app.admin.DevicePolicyManager; import android.app.trust.ITrustManager; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.PackageManager; import android.database.ContentObserver; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; @@ -809,12 +810,13 @@ public class BiometricService extends SystemService { public List<FingerprintSensorPropertiesInternal> getFingerprintSensorProperties( Context context) { - final FingerprintManager fpm = context.getSystemService(FingerprintManager.class); - if (fpm != null) { - return fpm.getSensorPropertiesInternal(); - } else { - return new ArrayList<>(); + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + final FingerprintManager fpm = context.getSystemService(FingerprintManager.class); + if (fpm != null) { + return fpm.getSensorPropertiesInternal(); + } } + return new ArrayList<>(); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java index 62c9295adda5..f07bf1e236b8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java @@ -53,7 +53,7 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub { @Override public byte[] dumpSensorServiceStateProto() throws RemoteException { - return mFaceService.dumpSensorServiceStateProto(); + return mFaceService.dumpSensorServiceStateProto(mSensorId); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 47897a1a0588..cb56e8cd4b7f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -127,17 +127,6 @@ public class FaceService extends SystemService implements BiometricServiceCallba return properties; } - @NonNull - private List<Face> getEnrolledFaces(int userId, String opPackageName) { - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); - if (provider == null) { - Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName); - return Collections.emptyList(); - } - - return provider.second.getEnrolledFaces(provider.first, userId); - } - /** * Receives the incoming binder calls from FaceManager. */ @@ -157,14 +146,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba } @Override - public byte[] dumpSensorServiceStateProto() { + public byte[] dumpSensorServiceStateProto(int sensorId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final ProtoOutputStream proto = new ProtoOutputStream(); - for (ServiceProvider provider : mServiceProviders) { - for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { - provider.dumpProtoState(props.sensorId, proto); - } + final ServiceProvider provider = getProviderForSensor(sensorId); + if (provider != null) { + provider.dumpProtoState(sensorId, proto); } proto.flush(); return proto.getBytes(); @@ -439,6 +427,7 @@ public class FaceService extends SystemService implements BiometricServiceCallba pw.println("Dumping for sensorId: " + props.sensorId + ", provider: " + provider.getClass().getSimpleName()); provider.dumpInternal(props.sensorId, pw); + pw.println(); } } } @@ -472,7 +461,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); } - return FaceService.this.getEnrolledFaces(userId, opPackageName); + final ServiceProvider provider = getProviderForSensor(sensorId); + if (provider == null) { + Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName); + return Collections.emptyList(); + } + + return provider.getEnrolledFaces(sensorId, userId); } @Override // Binder call @@ -483,7 +478,16 @@ public class FaceService extends SystemService implements BiometricServiceCallba Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); } - return !FaceService.this.getEnrolledFaces(userId, opPackageName).isEmpty(); + final ServiceProvider provider = getProviderForSensor(sensorId); + if (provider == null) { + Slog.w(TAG, "Null provider for hasEnrolledFaces, caller: " + opPackageName); + return false; + } + + final boolean enrolled = provider.getEnrolledFaces(sensorId, userId).size() > 0; + Slog.d(TAG, "hasEnrolledFaces, sensor: " + sensorId + ", enrolled: " + enrolled); + + return provider.getEnrolledFaces(sensorId, userId).size() > 0; } @Override // Binder call diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java index a0ffe58c779b..a0cd4a56ea12 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java @@ -72,13 +72,13 @@ public class FaceUtils implements BiometricUtils<Face> { } /** - * Legacy getter for {@link android.hardware.biometrics.face.V1_0} and its extended subclasses, - * which do not support a well defined sensorId from the HAL. + * Legacy getter for {@link android.hardware.biometrics.face.V1_0} and its extended subclasses. + * Framework-side cache is always stored in the same file, regardless of sensorId. */ - public static FaceUtils getInstance() { + public static FaceUtils getLegacyInstance(int sensorId) { // Note that sensorId for legacy services can be hard-coded to 0 since it's only used // to index into the sensor states map. - return getInstance(0 /* sensorId */, LEGACY_FACE_FILE); + return getInstance(sensorId, LEGACY_FACE_FILE); } private FaceUtils(String fileName) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java index 4c983fb5fa6c..9ed8f789aaa2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java @@ -153,7 +153,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); // Fake authentication with any of the existing fingers - List<Face> faces = FaceUtils.getInstance().getBiometricsForUser(mContext, userId); + List<Face> faces = FaceUtils.getLegacyInstance(mSensorId) + .getBiometricsForUser(mContext, userId); if (faces.isEmpty()) { Slog.w(TAG, "No faces, returning"); return; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index d384bc645d61..c4e4d1fe0f82 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -166,7 +166,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override public void onEnrollResult(long deviceId, int faceId, int userId, int remaining) { mHandler.post(() -> { - final CharSequence name = FaceUtils.getInstance() + final CharSequence name = FaceUtils.getLegacyInstance(mSensorId) .getUniqueName(mContext, userId); final Face face = new Face(name, faceId, deviceId); @@ -471,7 +471,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override @NonNull public List<Face> getEnrolledFaces(int sensorId, int userId) { - return FaceUtils.getInstance().getBiometricsForUser(mContext, userId); + return FaceUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId); } @Override @@ -610,8 +610,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, - opPackageName, FaceUtils.getInstance(), disabledFeatures, ENROLL_TIMEOUT_SEC, - surfaceHandle, mSensorId); + opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures, + ENROLL_TIMEOUT_SEC, surfaceHandle, mSensorId); mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { @Override @@ -665,7 +665,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName, - FaceUtils.getInstance(), mSensorId, mAuthenticatorIds); + FaceUtils.getLegacyInstance(mSensorId), mSensorId, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); } @@ -748,7 +748,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final List<Face> enrolledList = getEnrolledFaces(mSensorId, userId); final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext, mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, enrolledList, - FaceUtils.getInstance(), mAuthenticatorIds); + FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); } @@ -777,7 +777,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final long userToken = proto.start(SensorStateProto.USER_STATES); proto.write(UserStateProto.USER_ID, userId); - proto.write(UserStateProto.NUM_ENROLLED, FaceUtils.getInstance() + proto.write(UserStateProto.NUM_ENROLLED, FaceUtils.getLegacyInstance(mSensorId) .getBiometricsForUser(mContext, userId).size()); proto.end(userToken); } @@ -801,7 +801,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { JSONArray sets = new JSONArray(); for (UserInfo user : UserManager.get(mContext).getUsers()) { final int userId = user.getUserHandle().getIdentifier(); - final int c = FaceUtils.getInstance().getBiometricsForUser(mContext, userId).size(); + final int c = FaceUtils.getLegacyInstance(mSensorId) + .getBiometricsForUser(mContext, userId).size(); JSONObject set = new JSONObject(); set.put("id", userId); set.put("count", c); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java index 1616457e09a9..d4cdc8b18898 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java @@ -54,7 +54,7 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub @Override public byte[] dumpSensorServiceStateProto() throws RemoteException { - return mFingerprintService.dumpSensorServiceStateProto(); + return mFingerprintService.dumpSensorServiceStateProto(mSensorId); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 6e91c9a61d42..61f9cc40d233 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -115,15 +115,13 @@ public class FingerprintService extends SystemService implements BiometricServic } @Override - public byte[] dumpSensorServiceStateProto() { + public byte[] dumpSensorServiceStateProto(int sensorId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final ProtoOutputStream proto = new ProtoOutputStream(); - for (ServiceProvider provider : mServiceProviders) { - for (FingerprintSensorPropertiesInternal props - : provider.getSensorProperties()) { - provider.dumpProtoState(props.sensorId, proto); - } + final ServiceProvider provider = getProviderForSensor(sensorId); + if (provider != null) { + provider.dumpProtoState(sensorId, proto); } proto.flush(); return proto.getBytes(); @@ -437,6 +435,7 @@ public class FingerprintService extends SystemService implements BiometricServic pw.println("Dumping for sensorId: " + props.sensorId + ", provider: " + provider.getClass().getSimpleName()); provider.dumpInternal(props.sensorId, pw); + pw.println(); } } } @@ -588,13 +587,13 @@ public class FingerprintService extends SystemService implements BiometricServic @Nullable byte [] hardwareAuthToken, String opPackageName) { Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT); - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName); return; } - provider.second.scheduleResetLockout(sensorId, userId, hardwareAuthToken); + provider.scheduleResetLockout(sensorId, userId, hardwareAuthToken); } @Override @@ -747,14 +746,14 @@ public class FingerprintService extends SystemService implements BiometricServic } /** - * For devices with only a single provider, returns that provider. If no providers, or multiple - * providers exist, returns null. + * For devices with only a single provider, returns that provider. If multiple providers, + * returns the first one. If no providers, returns null. */ @Nullable private Pair<Integer, ServiceProvider> getSingleProvider() { final List<FingerprintSensorPropertiesInternal> properties = getSensorProperties(); - if (properties.size() != 1) { - Slog.e(TAG, "Multiple sensors found: " + properties.size()); + if (properties.isEmpty()) { + Slog.e(TAG, "No providers found"); return null; } @@ -767,7 +766,7 @@ public class FingerprintService extends SystemService implements BiometricServic } } - Slog.e(TAG, "Single sensor, but provider not found"); + Slog.e(TAG, "Provider not found"); return null; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java index 6da86502b64c..b3d2419901e4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java @@ -75,12 +75,12 @@ public class FingerprintUtils implements BiometricUtils<Fingerprint> { /** * Legacy getter for {@link android.hardware.biometrics.fingerprint.V2_1} ands its extended - * subclasses, which do not support a well defined sensorId from the HAL. + * subclasses. Framework-side cache is always stored in the same file, regardless of sensorId. */ - public static FingerprintUtils getInstance() { + public static FingerprintUtils getLegacyInstance(int sensorId) { // Note that sensorId for legacy services can be hard-coded to 0 since it's only used // to index into the sensor states map. - return getInstance(0 /* sensorId */, LEGACY_FINGERPRINT_FILE); + return getInstance(sensorId, LEGACY_FINGERPRINT_FILE); } private FingerprintUtils(String fileName) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index fd1181b616c7..3e13c45d335e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; @@ -59,6 +60,15 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { } @Override + public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) { + super.onEnrollResult(identifier, remaining); + + if (remaining == 0) { + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + } + } + + @Override protected boolean hasReachedEnrollmentLimit() { return FingerprintUtils.getInstance(getSensorId()) .getBiometricsForUser(getContext(), getTargetUserId()).size() diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java index 65ce34d31b61..74549b917e82 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java @@ -144,7 +144,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); // Fake authentication with any of the existing fingers - List<Fingerprint> fingerprints = FingerprintUtils.getInstance() + List<Fingerprint> fingerprints = FingerprintUtils.getLegacyInstance(mSensorId) .getBiometricsForUser(mContext, userId); if (fingerprints.isEmpty()) { Slog.w(TAG, "No fingerprints, returning"); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 7c5b7c92c1c6..b8d27aa61806 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -111,6 +111,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull private final HalResultController mHalResultController; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; private int mCurrentUserId = UserHandle.USER_NULL; + private final int mSensorId; private final class BiometricTaskStackListener extends TaskStackListener { @Override @@ -167,13 +168,15 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider void onHardwareUnavailable(); } + private final int mSensorId; @NonNull private final Context mContext; @NonNull final Handler mHandler; @NonNull final BiometricScheduler mScheduler; @Nullable private Callback mCallback; - HalResultController(@NonNull Context context, @NonNull Handler handler, + HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler, @NonNull BiometricScheduler scheduler) { + mSensorId = sensorId; mContext = context; mHandler = handler; mScheduler = scheduler; @@ -194,7 +197,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } final int currentUserId = client.getTargetUserId(); - final CharSequence name = FingerprintUtils.getInstance() + final CharSequence name = FingerprintUtils.getLegacyInstance(mSensorId) .getUniqueName(mContext, currentUserId); final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId); @@ -307,6 +310,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull HalResultController controller) { mContext = context; + mSensorId = sensorId; mScheduler = scheduler; mHandler = handler; mActivityTaskManager = ActivityTaskManager.getInstance(); @@ -361,7 +365,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final Handler handler = new Handler(Looper.getMainLooper()); final BiometricScheduler scheduler = new BiometricScheduler(TAG, gestureAvailabilityDispatcher); - final HalResultController controller = new HalResultController(context, handler, scheduler); + final HalResultController controller = new HalResultController(sensorId, context, handler, + scheduler); return new Fingerprint21(context, scheduler, handler, sensorId, strength, lockoutResetDispatcher, controller); } @@ -549,7 +554,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, - hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(), + hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController); mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { @Override @@ -624,7 +629,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId, - userId, opPackageName, FingerprintUtils.getInstance(), + userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), mSensorProperties.sensorId, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); @@ -638,8 +643,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, userId); final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient( mContext, mLazyDaemon, userId, mContext.getOpPackageName(), - mSensorProperties.sensorId, enrolledList, FingerprintUtils.getInstance(), - mAuthenticatorIds); + mSensorProperties.sensorId, enrolledList, + FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); } @@ -657,14 +662,15 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void rename(int sensorId, int fingerId, int userId, @NonNull String name) { mHandler.post(() -> { - FingerprintUtils.getInstance().renameBiometricForUser(mContext, userId, fingerId, name); + FingerprintUtils.getLegacyInstance(mSensorId) + .renameBiometricForUser(mContext, userId, fingerId, name); }); } @Override @NonNull public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) { - return FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId); + return FingerprintUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId); } @Override @@ -716,7 +722,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final long userToken = proto.start(SensorStateProto.USER_STATES); proto.write(UserStateProto.USER_ID, userId); - proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getInstance() + proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getLegacyInstance(mSensorId) .getBiometricsForUser(mContext, userId).size()); proto.end(userToken); } @@ -737,7 +743,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider proto.write(FingerprintUserStatsProto.USER_ID, userId); proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS, - FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId).size()); + FingerprintUtils.getLegacyInstance(mSensorId) + .getBiometricsForUser(mContext, userId).size()); // Normal fingerprint authentications (e.g. lockscreen) long countsToken = proto.start(FingerprintUserStatsProto.NORMAL); @@ -777,7 +784,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider JSONArray sets = new JSONArray(); for (UserInfo user : UserManager.get(mContext).getUsers()) { final int userId = user.getUserHandle().getIdentifier(); - final int N = FingerprintUtils.getInstance() + final int N = FingerprintUtils.getLegacyInstance(mSensorId) .getBiometricsForUser(mContext, userId).size(); JSONObject set = new JSONObject(); set.put("id", userId); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index e4933e40ccd5..791d224b9728 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -210,9 +210,9 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage @NonNull private Fingerprint21UdfpsMock mFingerprint21; @Nullable private LastAuthArgs mLastAuthArgs; - MockHalResultController(@NonNull Context context, @NonNull Handler handler, + MockHalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler, @NonNull BiometricScheduler scheduler) { - super(context, handler, scheduler); + super(sensorId, context, handler, scheduler); } void init(@NonNull RestartAuthRunnable restartAuthRunnable, @@ -280,7 +280,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage final TestableBiometricScheduler scheduler = new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher); final MockHalResultController controller = - new MockHalResultController(context, handler, scheduler); + new MockHalResultController(sensorId, context, handler, scheduler); return new Fingerprint21UdfpsMock(context, scheduler, handler, sensorId, strength, lockoutResetDispatcher, controller); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index 2a89a880b680..af61a8be3052 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; @@ -100,6 +101,15 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint } @Override + public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) { + super.onEnrollResult(identifier, remaining); + + if (remaining == 0) { + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + } + } + + @Override public void onPointerDown(int x, int y, float minor, float major) { UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major); } diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index c7891865644a..1f0fb5e006b8 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -45,8 +45,8 @@ import android.os.ServiceSpecificException; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; +import android.util.Log; import android.util.Pair; -import android.util.Slog; import java.net.InetAddress; import java.util.Arrays; @@ -279,7 +279,7 @@ public class DnsManager { } public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) { - Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")"); + Log.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")"); return (cfg != null) ? mPrivateDnsMap.put(network.netId, cfg) : mPrivateDnsMap.remove(network.netId); @@ -389,7 +389,7 @@ public class DnsManager { mPrivateDnsValidationMap.remove(netId); } - Slog.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, " + Log.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, " + "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers), Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds, paramsParcel.successThreshold, paramsParcel.minSamples, @@ -400,7 +400,7 @@ public class DnsManager { try { mDnsResolver.setResolverConfiguration(paramsParcel); } catch (RemoteException | ServiceSpecificException e) { - Slog.e(TAG, "Error setting DNS configuration: " + e); + Log.e(TAG, "Error setting DNS configuration: " + e); return; } } @@ -431,8 +431,8 @@ public class DnsManager { DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS); if (mSampleValidity < 0 || mSampleValidity > 65535) { - Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" + - DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS); + Log.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" + + DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS); mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS; } @@ -440,17 +440,17 @@ public class DnsManager { DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT); if (mSuccessThreshold < 0 || mSuccessThreshold > 100) { - Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" + - DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT); + Log.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" + + DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT); mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT; } mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES); mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES); if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) { - Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples + - "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " + - DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")"); + Log.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples + + "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " + + DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")"); mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES; mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES; } diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java index 17828a0549b3..21ef356c962c 100644 --- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java +++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java @@ -60,8 +60,8 @@ import android.os.UserHandle; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.DebugUtils; +import android.util.Log; import android.util.Range; -import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -230,7 +230,7 @@ public class MultipathPolicyTracker { mUsageCallback = new UsageCallback() { @Override public void onThresholdReached(int networkType, String subscriberId) { - if (DBG) Slog.d(TAG, "onThresholdReached for network " + network); + if (DBG) Log.d(TAG, "onThresholdReached for network " + network); mMultipathBudget = 0; updateMultipathBudget(); } @@ -252,7 +252,7 @@ public class MultipathPolicyTracker { final long bytes = getNetworkTotalBytes( start.toInstant().toEpochMilli(), end.toInstant().toEpochMilli()); - if (DBG) Slog.d(TAG, "Non-default data usage: " + bytes); + if (DBG) Log.d(TAG, "Non-default data usage: " + bytes); return bytes; } @@ -261,7 +261,7 @@ public class MultipathPolicyTracker { return LocalServices.getService(NetworkStatsManagerInternal.class) .getNetworkTotalBytes(mNetworkTemplate, start, end); } catch (RuntimeException e) { - Slog.w(TAG, "Failed to get data usage: " + e); + Log.w(TAG, "Failed to get data usage: " + e); return -1; } } @@ -326,17 +326,17 @@ public class MultipathPolicyTracker { void updateMultipathBudget() { long quota = LocalServices.getService(NetworkPolicyManagerInternal.class) .getSubscriptionOpportunisticQuota(this.network, QUOTA_TYPE_MULTIPATH); - if (DBG) Slog.d(TAG, "Opportunistic quota from data plan: " + quota + " bytes"); + if (DBG) Log.d(TAG, "Opportunistic quota from data plan: " + quota + " bytes"); // Fallback to user settings-based quota if not available from phone plan if (quota == OPPORTUNISTIC_QUOTA_UNKNOWN) { quota = getUserPolicyOpportunisticQuotaBytes(); - if (DBG) Slog.d(TAG, "Opportunistic quota from user policy: " + quota + " bytes"); + if (DBG) Log.d(TAG, "Opportunistic quota from user policy: " + quota + " bytes"); } if (quota == OPPORTUNISTIC_QUOTA_UNKNOWN) { quota = getDefaultDailyMultipathQuotaBytes(); - if (DBG) Slog.d(TAG, "Setting quota: " + quota + " bytes"); + if (DBG) Log.d(TAG, "Setting quota: " + quota + " bytes"); } // TODO: re-register if day changed: budget may have run out but should be refreshed. @@ -344,7 +344,7 @@ public class MultipathPolicyTracker { // If there is already a usage callback pending , there's no need to re-register it // if the quota hasn't changed. The callback will simply fire as expected when the // budget is spent. - if (DBG) Slog.d(TAG, "Quota still " + quota + ", not updating."); + if (DBG) Log.d(TAG, "Quota still " + quota + ", not updating."); return; } mQuota = quota; @@ -364,8 +364,9 @@ public class MultipathPolicyTracker { // since last time, so even if this is called very often the budget will not snap to 0 // as soon as there are less than 2MB left for today. if (budget > NetworkStatsManager.MIN_THRESHOLD_BYTES) { - if (DBG) Slog.d(TAG, "Setting callback for " + budget + - " bytes on network " + network); + if (DBG) { + Log.d(TAG, "Setting callback for " + budget + " bytes on network " + network); + } registerUsageCallback(budget); } else { maybeUnregisterUsageCallback(); @@ -402,7 +403,7 @@ public class MultipathPolicyTracker { private void maybeUnregisterUsageCallback() { if (haveMultipathBudget()) { - if (DBG) Slog.d(TAG, "Unregistering callback, budget was " + mMultipathBudget); + if (DBG) Log.d(TAG, "Unregistering callback, budget was " + mMultipathBudget); mStatsManager.unregisterUsageCallback(mUsageCallback); mMultipathBudget = 0; } @@ -467,9 +468,9 @@ public class MultipathPolicyTracker { try { mMultipathTrackers.put(network, new MultipathTracker(network, nc)); } catch (IllegalStateException e) { - Slog.e(TAG, "Can't track mobile network " + network + ": " + e.getMessage()); + Log.e(TAG, "Can't track mobile network " + network + ": " + e.getMessage()); } - if (DBG) Slog.d(TAG, "Tracking mobile network " + network); + if (DBG) Log.d(TAG, "Tracking mobile network " + network); } @Override @@ -479,7 +480,7 @@ public class MultipathPolicyTracker { existing.shutdown(); mMultipathTrackers.remove(network); } - if (DBG) Slog.d(TAG, "No longer tracking mobile network " + network); + if (DBG) Log.d(TAG, "No longer tracking mobile network " + network); } }; @@ -524,16 +525,16 @@ public class MultipathPolicyTracker { @Override public void onChange(boolean selfChange) { - Slog.wtf(TAG, "Should never be reached."); + Log.wtf(TAG, "Should never be reached."); } @Override public void onChange(boolean selfChange, Uri uri) { if (!Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES) .equals(uri)) { - Slog.wtf(TAG, "Unexpected settings observation: " + uri); + Log.wtf(TAG, "Unexpected settings observation: " + uri); } - if (DBG) Slog.d(TAG, "Settings change: updating budgets."); + if (DBG) Log.d(TAG, "Settings change: updating budgets."); updateAllMultipathBudgets(); } } @@ -541,7 +542,7 @@ public class MultipathPolicyTracker { private final class ConfigChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - if (DBG) Slog.d(TAG, "Configuration change: updating budgets."); + if (DBG) Log.d(TAG, "Configuration change: updating budgets."); updateAllMultipathBudgets(); } } diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 163788f4a4df..d9c2e80aadcd 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -30,7 +30,7 @@ import android.net.RouteInfo; import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.ServiceSpecificException; -import android.util.Slog; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -176,7 +176,7 @@ public class Nat464Xlat extends BaseNetworkObserver { try { mNMService.registerObserver(this); } catch (RemoteException e) { - Slog.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString()); + Log.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString()); return; } @@ -185,7 +185,7 @@ public class Nat464Xlat extends BaseNetworkObserver { try { addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString()); } catch (RemoteException | ServiceSpecificException e) { - Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e); + Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e); } mIface = CLAT_PREFIX + baseIface; mBaseIface = baseIface; @@ -193,7 +193,7 @@ public class Nat464Xlat extends BaseNetworkObserver { try { mIPv6Address = (Inet6Address) InetAddresses.parseNumericAddress(addrStr); } catch (ClassCastException | IllegalArgumentException | NullPointerException e) { - Slog.e(TAG, "Invalid IPv6 address " + addrStr); + Log.e(TAG, "Invalid IPv6 address " + addrStr); } if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) { stopPrefixDiscovery(); @@ -218,7 +218,7 @@ public class Nat464Xlat extends BaseNetworkObserver { try { mNMService.unregisterObserver(this); } catch (RemoteException | IllegalStateException e) { - Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e); + Log.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e); } mNat64PrefixInUse = null; mIface = null; @@ -242,37 +242,37 @@ public class Nat464Xlat extends BaseNetworkObserver { @VisibleForTesting protected void start() { if (isStarted()) { - Slog.e(TAG, "startClat: already started"); + Log.e(TAG, "startClat: already started"); return; } if (mNetwork.linkProperties == null) { - Slog.e(TAG, "startClat: Can't start clat with null LinkProperties"); + Log.e(TAG, "startClat: Can't start clat with null LinkProperties"); return; } String baseIface = mNetwork.linkProperties.getInterfaceName(); if (baseIface == null) { - Slog.e(TAG, "startClat: Can't start clat on null interface"); + Log.e(TAG, "startClat: Can't start clat on null interface"); return; } // TODO: should we only do this if mNetd.clatdStart() succeeds? - Slog.i(TAG, "Starting clatd on " + baseIface); + Log.i(TAG, "Starting clatd on " + baseIface); enterStartingState(baseIface); } @VisibleForTesting protected void stop() { if (!isStarted()) { - Slog.e(TAG, "stopClat: already stopped"); + Log.e(TAG, "stopClat: already stopped"); return; } - Slog.i(TAG, "Stopping clatd on " + mBaseIface); + Log.i(TAG, "Stopping clatd on " + mBaseIface); try { mNetd.clatdStop(mBaseIface); } catch (RemoteException | ServiceSpecificException e) { - Slog.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e); + Log.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e); } String iface = mIface; @@ -294,7 +294,7 @@ public class Nat464Xlat extends BaseNetworkObserver { try { mDnsResolver.startPrefix64Discovery(getNetId()); } catch (RemoteException | ServiceSpecificException e) { - Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e); + Log.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e); } mPrefixDiscoveryRunning = true; } @@ -303,7 +303,7 @@ public class Nat464Xlat extends BaseNetworkObserver { try { mDnsResolver.stopPrefix64Discovery(getNetId()); } catch (RemoteException | ServiceSpecificException e) { - Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e); + Log.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e); } mPrefixDiscoveryRunning = false; } @@ -320,7 +320,7 @@ public class Nat464Xlat extends BaseNetworkObserver { try { mDnsResolver.setPrefix64(getNetId(), prefixString); } catch (RemoteException | ServiceSpecificException e) { - Slog.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to " + Log.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to " + prefix + ": " + e); } } @@ -328,7 +328,7 @@ public class Nat464Xlat extends BaseNetworkObserver { private void maybeHandleNat64PrefixChange() { final IpPrefix newPrefix = selectNat64Prefix(); if (!Objects.equals(mNat64PrefixInUse, newPrefix)) { - Slog.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to " + Log.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to " + newPrefix); stop(); // It's safe to call update here, even though this method is called from update, because @@ -418,7 +418,7 @@ public class Nat464Xlat extends BaseNetworkObserver { return; } - Slog.d(TAG, "clatd running, updating NAI for " + mIface); + Log.d(TAG, "clatd running, updating NAI for " + mIface); for (LinkProperties stacked: oldLp.getStackedLinks()) { if (Objects.equals(mIface, stacked.getInterfaceName())) { lp.addStackedLink(stacked); @@ -451,7 +451,7 @@ public class Nat464Xlat extends BaseNetworkObserver { return new LinkAddress( InetAddresses.parseNumericAddress(config.ipv4Addr), config.prefixLength); } catch (IllegalArgumentException | RemoteException | ServiceSpecificException e) { - Slog.e(TAG, "Error getting link properties: " + e); + Log.e(TAG, "Error getting link properties: " + e); return null; } } @@ -480,11 +480,11 @@ public class Nat464Xlat extends BaseNetworkObserver { LinkAddress clatAddress = getLinkAddress(iface); if (clatAddress == null) { - Slog.e(TAG, "clatAddress was null for stacked iface " + iface); + Log.e(TAG, "clatAddress was null for stacked iface " + iface); return; } - Slog.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s", + Log.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s", mIface, mIface, mBaseIface)); enterRunningState(); LinkProperties lp = new LinkProperties(mNetwork.linkProperties); @@ -503,7 +503,7 @@ public class Nat464Xlat extends BaseNetworkObserver { return; } - Slog.i(TAG, "interface " + iface + " removed"); + Log.i(TAG, "interface " + iface + " removed"); // If we're running, and the interface was removed, then we didn't call stop(), and it's // likely that clatd crashed. Ensure we call stop() so we can start clatd again. Calling // stop() will also update LinkProperties, and if clatd crashed, the LinkProperties update diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 7f4fb4039d4e..7795ed38a74f 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -35,7 +35,7 @@ import android.os.UserHandle; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; -import android.util.Slog; +import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; import android.widget.Toast; @@ -175,7 +175,7 @@ public class NetworkNotificationManager { final int previousEventId = mNotificationTypeMap.get(id); final NotificationType previousNotifyType = NotificationType.getFromId(previousEventId); if (priority(previousNotifyType) > priority(notifyType)) { - Slog.d(TAG, String.format( + Log.d(TAG, String.format( "ignoring notification %s for network %s with existing notification %s", notifyType, id, previousNotifyType)); return; @@ -183,7 +183,7 @@ public class NetworkNotificationManager { clearNotification(id); if (DBG) { - Slog.d(TAG, String.format( + Log.d(TAG, String.format( "showNotification tag=%s event=%s transport=%s name=%s highPriority=%s", tag, nameOf(eventId), getTransportName(transportType), name, highPriority)); } @@ -253,7 +253,7 @@ public class NetworkNotificationManager { // are sent, but they are not implemented yet. return; } else { - Slog.wtf(TAG, "Unknown notification type " + notifyType + " on network transport " + Log.wtf(TAG, "Unknown notification type " + notifyType + " on network transport " + getTransportName(transportType)); return; } @@ -294,7 +294,7 @@ public class NetworkNotificationManager { try { mNotificationManager.notify(tag, eventId, notification); } catch (NullPointerException npe) { - Slog.d(TAG, "setNotificationVisible: visible notificationManager error", npe); + Log.d(TAG, "setNotificationVisible: visible notificationManager error", npe); } } @@ -317,13 +317,13 @@ public class NetworkNotificationManager { final String tag = tagFor(id); final int eventId = mNotificationTypeMap.get(id); if (DBG) { - Slog.d(TAG, String.format("clearing notification tag=%s event=%s", tag, + Log.d(TAG, String.format("clearing notification tag=%s event=%s", tag, nameOf(eventId))); } try { mNotificationManager.cancel(tag, eventId); } catch (NullPointerException npe) { - Slog.d(TAG, String.format( + Log.d(TAG, String.format( "failed to clear notification tag=%s event=%s", tag, nameOf(eventId)), npe); } mNotificationTypeMap.delete(id); diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java index 26cc3ee165f1..5cb3d94a929f 100644 --- a/services/core/java/com/android/server/connectivity/ProxyTracker.java +++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java @@ -35,7 +35,7 @@ import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; -import android.util.Slog; +import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -168,7 +168,7 @@ public class ProxyTracker { proxyProperties = new ProxyInfo(host, port, exclList); } if (!proxyProperties.isValid()) { - if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties); + if (DBG) Log.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties); return; } @@ -223,7 +223,7 @@ public class ProxyTracker { if (mPacManager.setCurrentProxyScriptUrl(proxyInfo) == PacManager.DONT_SEND_BROADCAST) { return; } - if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo); + if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo); Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); @@ -255,7 +255,7 @@ public class ProxyTracker { if (proxyInfo != null && (!TextUtils.isEmpty(proxyInfo.getHost()) || !Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))) { if (!proxyInfo.isValid()) { - if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo); + if (DBG) Log.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo); return; } mGlobalProxy = new ProxyInfo(proxyInfo); @@ -296,7 +296,7 @@ public class ProxyTracker { synchronized (mProxyLock) { if (Objects.equals(mDefaultProxy, proxyInfo)) return; if (proxyInfo != null && !proxyInfo.isValid()) { - if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo); + if (DBG) Log.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo); return; } diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index a2c427b8036a..027b9afba392 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -76,8 +76,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; -import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -296,8 +295,8 @@ public final class ContentService extends IContentService.Stub { // Let the package manager query for the sync adapters for a given authority // as we grant default permissions to sync adapters for specific authorities. - final PermissionManagerServiceInternal permissionManagerInternal = - LocalServices.getService(PermissionManagerServiceInternal.class); + final LegacyPermissionManagerInternal permissionManagerInternal = + LocalServices.getService(LegacyPermissionManagerInternal.class); permissionManagerInternal.setSyncAdapterPackagesProvider((authority, userId) -> { return getSyncAdapterPackagesForAuthorityAsUser(authority, userId); }); diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index d27cb16ecc51..668142b7e8ee 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -1804,10 +1804,8 @@ public class SyncStorageEngine { int id = -1; try { id = parser.getAttributeInt(null, "id"); - } catch (NumberFormatException e) { + } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing the id of the authority", e); - } catch (NullPointerException e) { - Slog.e(TAG, "the id of the authority is null", e); } if (id >= 0) { String authorityName = parser.getAttributeValue(null, "authority"); @@ -1920,28 +1918,26 @@ public class SyncStorageEngine { private void parseExtra(TypedXmlPullParser parser, Bundle extras) { String name = parser.getAttributeValue(null, "name"); String type = parser.getAttributeValue(null, "type"); - String value1 = parser.getAttributeValue(null, "value1"); - String value2 = parser.getAttributeValue(null, "value2"); try { if ("long".equals(type)) { - extras.putLong(name, Long.parseLong(value1)); + extras.putLong(name, parser.getAttributeLong(null, "value1")); } else if ("integer".equals(type)) { - extras.putInt(name, Integer.parseInt(value1)); + extras.putInt(name, parser.getAttributeInt(null, "value1")); } else if ("double".equals(type)) { - extras.putDouble(name, Double.parseDouble(value1)); + extras.putDouble(name, parser.getAttributeDouble(null, "value1")); } else if ("float".equals(type)) { - extras.putFloat(name, Float.parseFloat(value1)); + extras.putFloat(name, parser.getAttributeFloat(null, "value1")); } else if ("boolean".equals(type)) { - extras.putBoolean(name, Boolean.parseBoolean(value1)); + extras.putBoolean(name, parser.getAttributeBoolean(null, "value1")); } else if ("string".equals(type)) { - extras.putString(name, value1); + extras.putString(name, parser.getAttributeValue(null, "value1")); } else if ("account".equals(type)) { + final String value1 = parser.getAttributeValue(null, "value1"); + final String value2 = parser.getAttributeValue(null, "value2"); extras.putParcelable(name, new Account(value1, value2)); } - } catch (NumberFormatException e) { - Slog.e(TAG, "error parsing bundle value", e); - } catch (NullPointerException e) { + } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing bundle value", e); } } diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 2a0e21919704..7a8ba9f4380c 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -668,12 +668,11 @@ public class BrightnessTracker { builder.setUserBrightnessPoint( parser.getAttributeBoolean(null, ATTR_USER_POINT, false)); - String colorSampleDurationString = - parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION); + long colorSampleDuration = + parser.getAttributeLong(null, ATTR_COLOR_SAMPLE_DURATION, -1); String colorValueBucketsString = parser.getAttributeValue(null, ATTR_COLOR_VALUE_BUCKETS); - if (colorSampleDurationString != null && colorValueBucketsString != null) { - long colorSampleDuration = Long.parseLong(colorSampleDurationString); + if (colorSampleDuration != -1 && colorValueBucketsString != null) { String[] buckets = colorValueBucketsString.split(","); long[] bucketValues = new long[buckets.length]; for (int i = 0; i < bucketValues.length; ++i) { diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 23e5a43a359f..a8e56a1187cf 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -73,15 +73,31 @@ public class DisplayDeviceConfig { public static DisplayDeviceConfig create(long physicalDisplayId) { DisplayDeviceConfig config; + config = loadConfigFromDirectory(Environment.getProductDirectory(), physicalDisplayId); + if (config != null) { + return config; + } + + config = loadConfigFromDirectory(Environment.getVendorDirectory(), physicalDisplayId); + if (config != null) { + return config; + } + + return null; + } + + private static DisplayDeviceConfig loadConfigFromDirectory( + File baseDirectory, long physicalDisplayId) { + DisplayDeviceConfig config; // Create config using filename from physical ID (including "stable" bit). - config = getConfigFromSuffix(STABLE_ID_SUFFIX_FORMAT, physicalDisplayId); + config = getConfigFromSuffix(baseDirectory, STABLE_ID_SUFFIX_FORMAT, physicalDisplayId); if (config != null) { return config; } // Create config using filename from physical ID (excluding "stable" bit). final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG; - config = getConfigFromSuffix(NO_SUFFIX_FORMAT, withoutStableFlag); + config = getConfigFromSuffix(baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag); if (config != null) { return config; } @@ -90,7 +106,7 @@ public class DisplayDeviceConfig { final DisplayAddress.Physical physicalAddress = DisplayAddress.fromPhysicalDisplayId(physicalDisplayId); int port = physicalAddress.getPort(); - config = getConfigFromSuffix(PORT_SUFFIX_FORMAT, port); + config = getConfigFromSuffix(baseDirectory, PORT_SUFFIX_FORMAT, port); if (config != null) { return config; } @@ -127,12 +143,13 @@ public class DisplayDeviceConfig { return str; } - private static DisplayDeviceConfig getConfigFromSuffix(String suffixFormat, long idNumber) { + private static DisplayDeviceConfig getConfigFromSuffix(File baseDirectory, + String suffixFormat, long idNumber) { final String suffix = String.format(suffixFormat, idNumber); final String filename = String.format(CONFIG_FILE_FORMAT, suffix); final File filePath = Environment.buildPath( - Environment.getProductDirectory(), ETC_DIR, DISPLAY_CONFIG_DIR, filename); + baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename); if (filePath.exists()) { final DisplayDeviceConfig config = new DisplayDeviceConfig(); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 29b413d921e1..d4a19d6bc366 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -53,6 +53,7 @@ import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.Curve; +import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; @@ -1252,11 +1253,19 @@ public final class DisplayManagerService extends SystemService { mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled); } - boolean shouldAlwaysRespectAppRequestedModeInternal() { return mDisplayModeDirector.shouldAlwaysRespectAppRequestedMode(); } + void setRefreshRateSwitchingTypeInternal(@DisplayManager.SwitchingType int newValue) { + mDisplayModeDirector.setModeSwitchingType(newValue); + } + + @DisplayManager.SwitchingType + int getRefreshRateSwitchingTypeInternal() { + return mDisplayModeDirector.getModeSwitchingType(); + } + private void setBrightnessConfigurationForUserInternal( @Nullable BrightnessConfiguration c, @UserIdInt int userId, @Nullable String packageName) { @@ -2595,6 +2604,32 @@ public final class DisplayManagerService extends SystemService { } } + @Override // Binder call + public void setRefreshRateSwitchingType(int newValue) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE, + "Permission required to modify refresh rate switching type."); + final long token = Binder.clearCallingIdentity(); + try { + setRefreshRateSwitchingTypeInternal(newValue); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public int getRefreshRateSwitchingType() { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE, + "Permission required read refresh rate switching type."); + final long token = Binder.clearCallingIdentity(); + try { + return getRefreshRateSwitchingTypeInternal(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean validatePackageName(int uid, String packageName) { if (packageName != null) { String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid); diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 329081a8391f..95918945eecd 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -16,7 +16,6 @@ package com.android.server.display; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; @@ -53,8 +52,6 @@ import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -110,28 +107,11 @@ public class DisplayModeDirector { private boolean mAlwaysRespectAppRequest; - @IntDef(prefix = {"SWITCHING_TYPE_"}, value = { - SWITCHING_TYPE_NONE, - SWITCHING_TYPE_WITHIN_GROUPS, - SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface SwitchingType {} - - // No mode switching will happen. - public static final int SWITCHING_TYPE_NONE = 0; - // Allow only refresh rate switching between modes in the same configuration group. This way - // only switches without visual interruptions for the user will be allowed. - public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; - // Allow refresh rate switching between all refresh rates even if the switch with have visual - // interruptions for the user. - public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; - /** * The allowed refresh rate switching type. This is used by SurfaceFlinger. */ - @SwitchingType - private int mModeSwitchingType = SWITCHING_TYPE_WITHIN_GROUPS; + @DisplayManager.SwitchingType + private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS; public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { this(context, handler, new RealInjector()); @@ -337,7 +317,7 @@ public class DisplayModeDirector { if (availableModes.length > 0) { baseModeId = availableModes[0]; } - if (mModeSwitchingType == SWITCHING_TYPE_NONE) { + if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) { Display.Mode baseMode = null; for (Display.Mode mode : modes) { if (mode.getModeId() == baseModeId) { @@ -359,7 +339,7 @@ public class DisplayModeDirector { } boolean allowGroupSwitching = - mModeSwitchingType == SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; + mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; return new DesiredDisplayModeSpecs(baseModeId, allowGroupSwitching, new RefreshRateRange( @@ -450,18 +430,21 @@ public class DisplayModeDirector { /** * Sets the display mode switching type. - * @param type + * @param newType */ - public void setModeSwitchingType(@SwitchingType int type) { + public void setModeSwitchingType(@DisplayManager.SwitchingType int newType) { synchronized (mLock) { - mModeSwitchingType = type; + if (newType != mModeSwitchingType) { + mModeSwitchingType = newType; + notifyDesiredDisplayModeSpecsChangedLocked(); + } } } /** * Returns the display mode switching type. */ - @SwitchingType + @DisplayManager.SwitchingType public int getModeSwitchingType() { synchronized (mLock) { return mModeSwitchingType; @@ -583,13 +566,13 @@ public class DisplayModeDirector { } } - private static String switchingTypeToString(@SwitchingType int type) { + private static String switchingTypeToString(@DisplayManager.SwitchingType int type) { switch (type) { - case SWITCHING_TYPE_NONE: + case DisplayManager.SWITCHING_TYPE_NONE: return "SWITCHING_TYPE_NONE"; - case SWITCHING_TYPE_WITHIN_GROUPS: + case DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS: return "SWITCHING_TYPE_WITHIN_GROUPS"; - case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS: + case DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS: return "SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS"; default: return "Unknown SwitchingType " + type; diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index b0820e81ec09..a62642b1f842 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -31,17 +31,12 @@ import android.util.Xml; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -49,7 +44,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -628,15 +622,7 @@ final class PersistentDataStore { } String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); - String timeStampString = parser.getAttributeValue(null, ATTR_TIME_STAMP); - long timeStamp = -1; - if (timeStampString != null) { - try { - timeStamp = Long.parseLong(timeStampString); - } catch (NumberFormatException nfe) { - // Ignore we will just not restore the timestamp. - } - } + long timeStamp = parser.getAttributeLong(null, ATTR_TIME_STAMP, -1); try { BrightnessConfiguration config = diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index 22921ad1ecc2..521ce6973522 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -64,6 +64,9 @@ public final class FontManagerService { @Nullable private SharedMemory getSerializedSystemFontMap() { + if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + return null; + } synchronized (FontManagerService.this) { if (mSerializedSystemFontMap == null) { mSerializedSystemFontMap = createSerializedSystemFontMapLocked(); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java index 2878a94fb860..a9eb75da77cb 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java @@ -18,9 +18,14 @@ package com.android.server.hdmi; import android.stats.hdmi.HdmiStatsEnums; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; -class HdmiCecAtomWriter { +/** + * Provides methods for writing HDMI-CEC statsd atoms. + */ +@VisibleForTesting +public class HdmiCecAtomWriter { private static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100; private static final int ERROR_CODE_UNKNOWN = -1; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index a261fa1f2741..98d130f1ef69 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -253,7 +253,7 @@ public class HdmiCecConfig { return STORAGE_GLOBAL_SETTINGS; case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION: return STORAGE_GLOBAL_SETTINGS; - case HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP: + case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE: return STORAGE_GLOBAL_SETTINGS; case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST: return STORAGE_SHARED_PREFS; @@ -271,7 +271,7 @@ public class HdmiCecConfig { return Global.HDMI_CONTROL_ENABLED; case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION: return Global.HDMI_CEC_VERSION; - case HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP: + case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE: return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP; case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST: return setting.getName(); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index 19dc017a35d5..bbd5ac3b5ccc 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -635,7 +635,7 @@ final class HdmiCecController { */ private int incomingMessageDirection(int srcAddress, int dstAddress) { boolean sourceIsLocal = false; - boolean destinationIsLocal = false; + boolean destinationIsLocal = dstAddress == Constants.ADDR_BROADCAST; for (HdmiCecLocalDevice localDevice : mService.getHdmiCecNetwork().getLocalDeviceList()) { int logicalAddress = localDevice.getDeviceInfo().getLogicalAddress(); if (logicalAddress == srcAddress) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 9ca1f913f5ce..e6cf18b8db3d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -160,20 +160,20 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { switch (standbyAction) { case HdmiControlService.STANDBY_SCREEN_OFF: // Get latest setting value - @HdmiControlManager.StandbyBehavior + @HdmiControlManager.PowerControlMode String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); switch (sendStandbyOnSleep) { - case HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV: + case HdmiControlManager.POWER_CONTROL_MODE_TV: mService.sendCecCommand( HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV)); break; - case HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST: + case HdmiControlManager.POWER_CONTROL_MODE_BROADCAST: mService.sendCecCommand( HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST)); break; - case HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE: + case HdmiControlManager.POWER_CONTROL_MODE_NONE: break; } break; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index 59ebdd597279..8a1a728ba8dd 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -107,8 +107,8 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { protected void sendStandby(int deviceId) { assertRunOnServiceThread(); String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); - if (sendStandbyOnSleep.equals(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST)) { + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); + if (sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)) { mService.sendCecCommand( HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST)); return; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java index 5d75a63d0c8d..fc21724714c5 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java @@ -59,7 +59,8 @@ import java.util.concurrent.ArrayBlockingQueue; * Note that the information cached in this class is not guaranteed to be up-to-date, especially OSD * names, power states can be outdated. */ -class HdmiCecNetwork { +@VisibleForTesting +public class HdmiCecNetwork { private static final String TAG = "HdmiCecNetwork"; protected final Object mLock; diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 56b73ba04d89..a1d13e974019 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -564,7 +564,8 @@ public class HdmiControlService extends SystemService { mTvInputManager.unregisterCallback(callback); } - PowerManager getPowerManager() { + @VisibleForTesting + protected PowerManager getPowerManager() { return mPowerManager; } @@ -729,7 +730,8 @@ public class HdmiControlService extends SystemService { Global.putInt(cr, key, toInt(value)); } - void writeStringSystemProperty(String key, String value) { + @VisibleForTesting + protected void writeStringSystemProperty(String key, String value) { SystemProperties.set(key, value); } @@ -3366,7 +3368,7 @@ public class HdmiControlService extends SystemService { } @VisibleForTesting - HdmiCecAtomWriter getAtomWriter() { + protected HdmiCecAtomWriter getAtomWriter() { return mAtomWriter; } @@ -3395,7 +3397,8 @@ public class HdmiControlService extends SystemService { HdmiControlService.PERMISSION); } - HdmiCecConfig getHdmiCecConfig() { + @VisibleForTesting + protected HdmiCecConfig getHdmiCecConfig() { return mHdmiCecConfig; } } diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java index f9f97fe7b184..ea6e61582ea0 100644 --- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java +++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java @@ -165,9 +165,9 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { if (service.isAudioSystemDevice()) { return false; } - @HdmiControlManager.StandbyBehavior String sendStandbyOnSleep = + @HdmiControlManager.PowerControlMode String sendStandbyOnSleep = service.getHdmiCecConfig().getStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); - return sendStandbyOnSleep.equals(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); + return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); } } diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java index a735a8f9d305..6cec2723b141 100644 --- a/services/core/java/com/android/server/input/PersistentDataStore.java +++ b/services/core/java/com/android/server/input/PersistentDataStore.java @@ -510,7 +510,7 @@ final class PersistentDataStore { serializer.startTag(null, "keyboard-layout"); serializer.attribute(null, "descriptor", layout); if (layout.equals(mCurrentKeyboardLayout)) { - serializer.attribute(null, "current", "true"); + serializer.attributeBoolean(null, "current", true); } serializer.endTag(null, "keyboard-layout"); } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index e5c198646fb6..c3a10c7a4f8a 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -113,7 +113,7 @@ import com.android.server.location.provider.MockLocationProvider; import com.android.server.location.provider.PassiveLocationProvider; import com.android.server.location.provider.PassiveLocationProviderManager; import com.android.server.location.provider.proxy.ProxyLocationProvider; -import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -261,8 +261,8 @@ public class LocationManagerService extends ILocationManager.Stub { // Let the package manager query which are the default location // providers as they get certain permissions granted by default. - PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService( - PermissionManagerServiceInternal.class); + LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService( + LegacyPermissionManagerInternal.class); permissionManagerInternal.setLocationPackagesProvider( userId -> mContext.getResources().getStringArray( com.android.internal.R.array.config_locationProviderPackageNames)); diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java index b4bcd7b10c42..fd8cf707e697 100644 --- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java +++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java @@ -55,7 +55,7 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { @Override void onInitialize() { - mProxy.setListener(new LocationTimeZoneProviderProxy.Listener() { + mProxy.initialize(new LocationTimeZoneProviderProxy.Listener() { @Override public void onReportLocationTimeZoneEvent( @NonNull LocationTimeZoneEvent locationTimeZoneEvent) { diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java index b7c7476844a5..c8a1db6681ff 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java @@ -19,6 +19,7 @@ package com.android.server.location.timezone; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; import android.os.Binder; import android.os.ResultReceiver; import android.os.ShellCallback; @@ -27,6 +28,7 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; import com.android.server.FgThread; @@ -179,36 +181,45 @@ public class LocationTimeZoneManagerService extends Binder { } private LocationTimeZoneProvider createPrimaryProvider() { + Resources resources = mContext.getResources(); + if (!resources.getBoolean(R.bool.config_enablePrimaryLocationTimeZoneProvider)) { + return new NullLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME); + } + LocationTimeZoneProviderProxy proxy; if (isInSimulationMode(PRIMARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { - proxy = RealLocationTimeZoneProviderProxy.createAndRegister( + proxy = new RealLocationTimeZoneProviderProxy( mContext, mThreadingDomain, PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION, - com.android.internal.R.bool.config_enablePrimaryLocationTimeZoneOverlay, - com.android.internal.R.string.config_primaryLocationTimeZoneProviderPackageName + R.bool.config_enablePrimaryLocationTimeZoneOverlay, + R.string.config_primaryLocationTimeZoneProviderPackageName ); } - return createLocationTimeZoneProvider(PRIMARY_PROVIDER_NAME, proxy); + return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy); } private LocationTimeZoneProvider createSecondaryProvider() { + Resources resources = mContext.getResources(); + if (!resources.getBoolean(R.bool.config_enableSecondaryLocationTimeZoneProvider)) { + return new NullLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME); + } + LocationTimeZoneProviderProxy proxy; if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { - proxy = RealLocationTimeZoneProviderProxy.createAndRegister( + proxy = new RealLocationTimeZoneProviderProxy( mContext, mThreadingDomain, SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION, - com.android.internal.R.bool.config_enableSecondaryLocationTimeZoneOverlay, - com.android.internal.R.string - .config_secondaryLocationTimeZoneProviderPackageName + R.bool.config_enableSecondaryLocationTimeZoneOverlay, + R.string.config_secondaryLocationTimeZoneProviderPackageName ); } - return createLocationTimeZoneProvider(SECONDARY_PROVIDER_NAME, proxy); + return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy); } private boolean isInSimulationMode(String providerName) { @@ -216,21 +227,6 @@ public class LocationTimeZoneManagerService extends Binder { SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + providerName, false); } - private LocationTimeZoneProvider createLocationTimeZoneProvider( - @NonNull String providerName, @NonNull LocationTimeZoneProviderProxy proxy) { - LocationTimeZoneProvider provider; - if (proxy != null) { - debugLog("LocationTimeZoneProvider found for providerName=" + providerName); - provider = new BinderLocationTimeZoneProvider(mThreadingDomain, - providerName, proxy); - } else { - debugLog("No LocationTimeZoneProvider found for providerName=" + providerName - + ": stubbing"); - provider = new NullLocationTimeZoneProvider(mThreadingDomain, providerName); - } - return provider; - } - @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java index 1b4706f8801e..8a0259d12a6c 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java @@ -70,9 +70,11 @@ abstract class LocationTimeZoneProviderProxy implements Dumpable { } /** - * Sets the listener. The listener can expect to receive all events after this point. + * Initializes the proxy. The supplied listener can expect to receive all events after this + * point. This method also calls {@link #onInitialize()} for subclasses to handle their own + * initialization. */ - void setListener(@NonNull Listener listener) { + void initialize(@NonNull Listener listener) { Objects.requireNonNull(listener); synchronized (mSharedLock) { if (mListener != null) { @@ -80,9 +82,15 @@ abstract class LocationTimeZoneProviderProxy implements Dumpable { } this.mListener = listener; } + onInitialize(); } /** + * Initializes the proxy. This is called after {@link #mListener} is set. + */ + abstract void onInitialize(); + + /** * Sets a new request for the provider. */ abstract void setRequest(@NonNull LocationTimeZoneProviderRequest request); diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java index cd6d3592af0e..1a012882305f 100644 --- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java @@ -41,23 +41,6 @@ import java.util.Objects; */ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy { - /** - * Creates and registers this proxy. If no suitable service is available for the proxy, returns - * null. - */ - @Nullable - static LocationTimeZoneProviderProxy createAndRegister( - @NonNull Context context, @NonNull ThreadingDomain threadingDomain, - @NonNull String action, int enableOverlayResId, int nonOverlayPackageResId) { - RealLocationTimeZoneProviderProxy proxy = new RealLocationTimeZoneProviderProxy( - context, threadingDomain, action, enableOverlayResId, nonOverlayPackageResId); - if (proxy.register()) { - return proxy; - } else { - return null; - } - } - @NonNull private final ServiceWatcher mServiceWatcher; @GuardedBy("mProxyLock") @@ -66,7 +49,7 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy { @GuardedBy("mProxyLock") @NonNull private LocationTimeZoneProviderRequest mRequest; - private RealLocationTimeZoneProviderProxy( + RealLocationTimeZoneProviderProxy( @NonNull Context context, @NonNull ThreadingDomain threadingDomain, @NonNull String action, int enableOverlayResId, int nonOverlayPackageResId) { @@ -77,6 +60,13 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy { enableOverlayResId, nonOverlayPackageResId); } + @Override + void onInitialize() { + if (!register()) { + throw new IllegalStateException("Unable to register binder proxy"); + } + } + private boolean register() { boolean resolves = mServiceWatcher.checkServiceResolves(); if (resolves) { diff --git a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java index 604ff74e71ac..5e66a99a93fa 100644 --- a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java @@ -49,6 +49,11 @@ class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderPro mRequest = LocationTimeZoneProviderRequest.EMPTY_REQUEST; } + @Override + void onInitialize() { + // No-op - nothing to do for the simulated provider. + } + void simulate(@NonNull SimulatedBinderProviderEvent event) { mThreadingDomain.assertCurrentThread(); diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index 64c3c289163e..ea1d8da74185 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -37,7 +37,7 @@ import android.os.Handler; import android.security.Credentials; import android.security.KeyStore; import android.text.TextUtils; -import android.util.Slog; +import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -128,7 +128,7 @@ public class LockdownVpnTracker { final int egressType = (egressInfo == null) ? TYPE_NONE : egressInfo.getType(); final String egressIface = (egressProp == null) ? null : egressProp.getInterfaceName(); - Slog.d(TAG, "handleStateChanged: egress=" + egressType + Log.d(TAG, "handleStateChanged: egress=" + egressType + " " + mAcceptedEgressIface + "->" + egressIface); if (egressDisconnected || egressChanged) { @@ -149,7 +149,7 @@ public class LockdownVpnTracker { } else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) { if (mProfile.isValidLockdownProfile()) { - Slog.d(TAG, "Active network connected; starting VPN"); + Log.d(TAG, "Active network connected; starting VPN"); EventLogTags.writeLockdownVpnConnecting(egressType); showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected); @@ -160,11 +160,11 @@ public class LockdownVpnTracker { mVpn.startLegacyVpnPrivileged(mProfile, KeyStore.getInstance(), egressProp); } catch (IllegalStateException e) { mAcceptedEgressIface = null; - Slog.e(TAG, "Failed to start VPN", e); + Log.e(TAG, "Failed to start VPN", e); showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected); } } else { - Slog.e(TAG, "Invalid VPN profile; requires IP-based server and DNS"); + Log.e(TAG, "Invalid VPN profile; requires IP-based server and DNS"); showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected); } @@ -172,8 +172,8 @@ public class LockdownVpnTracker { final String iface = vpnConfig.interfaze; final List<LinkAddress> sourceAddrs = vpnConfig.addresses; - Slog.d(TAG, "VPN connected using iface=" + iface + - ", sourceAddr=" + sourceAddrs.toString()); + Log.d(TAG, "VPN connected using iface=" + iface + + ", sourceAddr=" + sourceAddrs.toString()); EventLogTags.writeLockdownVpnConnected(egressType); showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected); @@ -190,7 +190,7 @@ public class LockdownVpnTracker { } private void initLocked() { - Slog.d(TAG, "initLocked()"); + Log.d(TAG, "initLocked()"); mVpn.setEnableTeardown(false); mVpn.setLockdown(true); @@ -204,7 +204,7 @@ public class LockdownVpnTracker { } private void shutdownLocked() { - Slog.d(TAG, "shutdownLocked()"); + Log.d(TAG, "shutdownLocked()"); mAcceptedEgressIface = null; mErrorCount = 0; @@ -222,7 +222,7 @@ public class LockdownVpnTracker { */ @GuardedBy("mConnService.mVpns") public void reset() { - Slog.d(TAG, "reset()"); + Log.d(TAG, "reset()"); synchronized (mStateLock) { // cycle tracker, reset error count, and trigger retry shutdownLocked(); diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 006d78e4a648..5bd352c8f8e8 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -17,11 +17,13 @@ package com.android.server.net; import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED; import static android.net.INetd.FIREWALL_CHAIN_STANDBY; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.os.Process.INVALID_UID; @@ -339,6 +341,8 @@ public class NetworkPolicyLogger { return FIREWALL_CHAIN_NAME_STANDBY; case FIREWALL_CHAIN_POWERSAVE: return FIREWALL_CHAIN_NAME_POWERSAVE; + case FIREWALL_CHAIN_RESTRICTED: + return FIREWALL_CHAIN_NAME_RESTRICTED; default: return String.valueOf(chain); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ee860e3ac6d7..eb7ef2bbeaf2 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -277,6 +277,7 @@ import com.android.server.pm.PackageManagerService; import com.android.server.policy.PhoneWindowManager; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.BackgroundActivityStartCallback; import com.android.server.wm.WindowManagerInternal; @@ -298,6 +299,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -371,6 +373,20 @@ public class NotificationManagerService extends SystemService { RoleManager.ROLE_EMERGENCY }; + // Used for rate limiting toasts by package. + static final String TOAST_QUOTA_TAG = "toast_quota_tag"; + + // This constant defines rate limits applied to showing toasts. The numbers are set in a way + // such that an aggressive toast showing strategy would result in a roughly 1.5x longer wait + // time (before the package is allowed to show toasts again) each time the toast rate limit is + // reached. It's meant to protect the user against apps spamming them with toasts (either + // accidentally or on purpose). + private static final MultiRateLimiter.RateLimit[] TOAST_RATE_LIMITS = { + MultiRateLimiter.RateLimit.create(3, Duration.ofSeconds(20)), + MultiRateLimiter.RateLimit.create(5, Duration.ofSeconds(42)), + MultiRateLimiter.RateLimit.create(6, Duration.ofSeconds(68)), + }; + // When #matchesCallFilter is called from the ringer, wait at most // 3s to resolve the contacts. This timeout is required since // ContactsProvider might take a long time to start up. @@ -422,6 +438,16 @@ public class NotificationManagerService extends SystemService { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L; + /** + * Rate limit showing toasts, on a per package basis. + * + * It limits the effects of {@link android.widget.Toast#show()} calls to prevent overburdening + * the user with too many toasts in a limited time. Any attempt to show more toasts than allowed + * in a certain time frame will result in the toast being discarded. + */ + @ChangeId + private static final long RATE_LIMIT_TOASTS = 154198299L; + private IActivityManager mAm; private ActivityTaskManagerInternal mAtm; private ActivityManager mActivityManager; @@ -500,6 +526,9 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mToastQueue") private boolean mIsCurrentToastShown = false; + // Used for rate limiting toasts by package. + private MultiRateLimiter mToastRateLimiter; + // The last key in this list owns the hardware. ArrayList<String> mLights = new ArrayList<>(); @@ -1662,6 +1691,11 @@ public class NotificationManagerService extends SystemService { = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_HISTORY_ENABLED); private final Uri NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI = Settings.Global.getUriFor(Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS); + private final Uri LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS + = Settings.Secure.getUriFor( + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + private final Uri LOCK_SCREEN_SHOW_NOTIFICATIONS + = Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS); SettingsObserver(Handler handler) { super(handler); @@ -1681,6 +1715,11 @@ public class NotificationManagerService extends SystemService { false, this, UserHandle.USER_ALL); resolver.registerContentObserver(NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI, false, this, UserHandle.USER_ALL); + + resolver.registerContentObserver(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LOCK_SCREEN_SHOW_NOTIFICATIONS, + false, this, UserHandle.USER_ALL); update(null); } @@ -1722,6 +1761,12 @@ public class NotificationManagerService extends SystemService { if (uri == null || NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI.equals(uri)) { mPreferencesHelper.updateMediaNotificationFilteringEnabled(); } + if (uri == null || LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS.equals(uri)) { + mPreferencesHelper.updateLockScreenPrivateNotifications(); + } + if (uri == null || LOCK_SCREEN_SHOW_NOTIFICATIONS.equals(uri)) { + mPreferencesHelper.updateLockScreenShowNotifications(); + } } } @@ -1915,7 +1960,8 @@ public class NotificationManagerService extends SystemService { DevicePolicyManagerInternal dpm, IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager, NotificationHistoryManager historyManager, StatsManager statsManager, - TelephonyManager telephonyManager, ActivityManagerInternal ami) { + TelephonyManager telephonyManager, ActivityManagerInternal ami, + MultiRateLimiter toastRateLimiter) { mHandler = handler; Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), @@ -2107,6 +2153,8 @@ public class NotificationManagerService extends SystemService { com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos)); mStatsManager = statsManager; + mToastRateLimiter = toastRateLimiter; + // register for various Intents. // If this is called within a test, make sure to unregister the intent receivers by // calling onDestroy() @@ -2217,7 +2265,8 @@ public class NotificationManagerService extends SystemService { mStatsManager = (StatsManager) getContext().getSystemService( Context.STATS_MANAGER), getContext().getSystemService(TelephonyManager.class), - LocalServices.getService(ActivityManagerInternal.class)); + LocalServices.getService(ActivityManagerInternal.class), + createToastRateLimiter()); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); @@ -2855,6 +2904,10 @@ public class NotificationManagerService extends SystemService { return mInternalService; } + private MultiRateLimiter createToastRateLimiter() { + return new MultiRateLimiter.Builder(getContext()).addRateLimits(TOAST_RATE_LIMITS).build(); + } + @VisibleForTesting final IBinder mService = new INotificationManager.Stub() { // Toasts @@ -3591,8 +3644,9 @@ public class NotificationManagerService extends SystemService { public ParceledListSlice<ConversationChannelWrapper> getConversations( boolean onlyImportant) { enforceSystemOrSystemUI("getConversations"); + IntArray userIds = mUserProfiles.getCurrentProfileIds(); ArrayList<ConversationChannelWrapper> conversations = - mPreferencesHelper.getConversations(onlyImportant); + mPreferencesHelper.getConversations(userIds, onlyImportant); for (ConversationChannelWrapper conversation : conversations) { if (mShortcutHelper == null) { conversation.setShortcutInfo(null); @@ -7115,15 +7169,15 @@ public class NotificationManagerService extends SystemService { if (record.getSbn().isGroup() && record.getNotification().suppressAlertingDueToGrouping()) { return false; } - // not if in call or the screen's on - if (isInCall() || mScreenOn) { + // not if in call + if (isInCall()) { return false; } // check current user if (!isNotificationForCurrentUser(record)) { return false; } - + // Light, but only when the screen is off return true; } @@ -7329,10 +7383,21 @@ public class NotificationManagerService extends SystemService { ToastRecord record = mToastQueue.get(0); while (record != null) { - if (record.show()) { + int userId = UserHandle.getUserId(record.uid); + boolean rateLimitingEnabled = + CompatChanges.isChangeEnabled(RATE_LIMIT_TOASTS, record.uid); + boolean isWithinQuota = + mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG); + if ((!rateLimitingEnabled || isWithinQuota) && record.show()) { scheduleDurationReachedLocked(record); mIsCurrentToastShown = true; + if (rateLimitingEnabled) { + mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG); + } return; + } else if (rateLimitingEnabled && !isWithinQuota) { + Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the " + + "following toast was blocked and discarded: " + record); } int index = mToastQueue.indexOf(record); if (index >= 0) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 1c0349d1b51f..cbd973aeaf6a 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -52,6 +52,7 @@ import android.service.notification.RankingHelperProto; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; @@ -170,6 +171,8 @@ public class PreferencesHelper implements RankingConfig { private final AppOpsManager mAppOps; private SparseBooleanArray mBadgingEnabled; + private SparseBooleanArray mLockScreenShowNotifications; + private SparseBooleanArray mLockScreenPrivateNotifications; private boolean mBubblesEnabledGlobally = DEFAULT_GLOBAL_ALLOW_BUBBLE; private boolean mIsMediaNotificationFilteringEnabled = DEFAULT_MEDIA_NOTIFICATION_FILTERING; private boolean mAreChannelsBypassingDnd; @@ -1379,36 +1382,39 @@ public class PreferencesHelper implements RankingConfig { return null; } - public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) { + public ArrayList<ConversationChannelWrapper> getConversations(IntArray userIds, + boolean onlyImportant) { synchronized (mPackagePreferences) { ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>(); - for (PackagePreferences p : mPackagePreferences.values()) { - int N = p.channels.size(); - for (int i = 0; i < N; i++) { - final NotificationChannel nc = p.channels.valueAt(i); - if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted() - && !nc.isDemoted() - && (nc.isImportantConversation() || !onlyImportant)) { - ConversationChannelWrapper conversation = new ConversationChannelWrapper(); - conversation.setPkg(p.pkg); - conversation.setUid(p.uid); - conversation.setNotificationChannel(nc); - conversation.setParentChannelLabel( - p.channels.get(nc.getParentChannelId()).getName()); - boolean blockedByGroup = false; - if (nc.getGroup() != null) { - NotificationChannelGroup group = p.groups.get(nc.getGroup()); - if (group != null) { - if (group.isBlocked()) { - blockedByGroup = true; - } else { - conversation.setGroupLabel(group.getName()); + if (userIds.binarySearch(UserHandle.getUserId(p.uid)) >= 0) { + int N = p.channels.size(); + for (int i = 0; i < N; i++) { + final NotificationChannel nc = p.channels.valueAt(i); + if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted() + && !nc.isDemoted() + && (nc.isImportantConversation() || !onlyImportant)) { + ConversationChannelWrapper conversation = + new ConversationChannelWrapper(); + conversation.setPkg(p.pkg); + conversation.setUid(p.uid); + conversation.setNotificationChannel(nc); + conversation.setParentChannelLabel( + p.channels.get(nc.getParentChannelId()).getName()); + boolean blockedByGroup = false; + if (nc.getGroup() != null) { + NotificationChannelGroup group = p.groups.get(nc.getGroup()); + if (group != null) { + if (group.isBlocked()) { + blockedByGroup = true; + } else { + conversation.setGroupLabel(group.getName()); + } } } - } - if (!blockedByGroup) { - conversations.add(conversation); + if (!blockedByGroup) { + conversations.add(conversation); + } } } } @@ -2401,6 +2407,60 @@ public class PreferencesHelper implements RankingConfig { return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE); } + public void updateLockScreenPrivateNotifications() { + if (mLockScreenPrivateNotifications == null) { + mLockScreenPrivateNotifications = new SparseBooleanArray(); + } + boolean changed = false; + // update the cached values + for (int index = 0; index < mLockScreenPrivateNotifications.size(); index++) { + int userId = mLockScreenPrivateNotifications.keyAt(index); + final boolean oldValue = mLockScreenPrivateNotifications.get(userId); + final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, userId) != 0; + mLockScreenPrivateNotifications.put(userId, newValue); + changed |= oldValue != newValue; + } + if (changed) { + updateConfig(); + } + } + + public void updateLockScreenShowNotifications() { + if (mLockScreenShowNotifications == null) { + mLockScreenShowNotifications = new SparseBooleanArray(); + } + boolean changed = false; + // update the cached values + for (int index = 0; index < mLockScreenShowNotifications.size(); index++) { + int userId = mLockScreenShowNotifications.keyAt(index); + final boolean oldValue = mLockScreenShowNotifications.get(userId); + final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, userId) != 0; + mLockScreenShowNotifications.put(userId, newValue); + changed |= oldValue != newValue; + } + if (changed) { + updateConfig(); + } + } + + @Override + public boolean canShowNotificationsOnLockscreen(int userId) { + if (mLockScreenShowNotifications == null) { + mLockScreenShowNotifications = new SparseBooleanArray(); + } + return mLockScreenShowNotifications.get(userId, true); + } + + @Override + public boolean canShowPrivateNotificationsOnLockScreen(int userId) { + if (mLockScreenPrivateNotifications == null) { + mLockScreenPrivateNotifications = new SparseBooleanArray(); + } + return mLockScreenPrivateNotifications.get(userId, true); + } + public void unlockAllNotificationChannels() { synchronized (mPackagePreferences) { final int numPackagePreferences = mPackagePreferences.size(); diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index b2827bae40bc..8991ced21fbd 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -34,6 +34,8 @@ public interface RankingConfig { /** Returns true when feature is enabled that shows media notifications in quick settings. */ boolean isMediaNotificationFilteringEnabled(); boolean isGroupBlocked(String packageName, int uid, String groupId); + boolean canShowNotificationsOnLockscreen(int userId); + boolean canShowPrivateNotificationsOnLockScreen(int userId); Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg, int uid); diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java index 9c3d6d352c89..b0be20698005 100644 --- a/services/core/java/com/android/server/notification/ShortcutHelper.java +++ b/services/core/java/com/android/server/notification/ShortcutHelper.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_PERSONS_DATA; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER; @@ -192,8 +193,8 @@ public class ShortcutHelper { LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery(); query.setPackage(packageName); query.setShortcutIds(Arrays.asList(shortcutId)); - query.setQueryFlags( - FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER | FLAG_MATCH_CACHED); + query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER + | FLAG_MATCH_CACHED | FLAG_GET_PERSONS_DATA); List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user); ShortcutInfo info = shortcuts != null && shortcuts.size() > 0 ? shortcuts.get(0) diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index b5ca2abf8afe..2122b9c19805 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -558,8 +558,8 @@ public class SnoozeHelper { if (value < currentTime) { return; } - out.attribute(null, XML_SNOOZED_NOTIFICATION_TIME, - value.toString()); + out.attributeLong(null, XML_SNOOZED_NOTIFICATION_TIME, + value); }); writeXml(out, mPersistedSnoozedNotificationsWithContext, XML_SNOOZED_NOTIFICATION_CONTEXT, diff --git a/services/core/java/com/android/server/notification/VisibilityExtractor.java b/services/core/java/com/android/server/notification/VisibilityExtractor.java index db548465d4b8..a363601b56e9 100644 --- a/services/core/java/com/android/server/notification/VisibilityExtractor.java +++ b/services/core/java/com/android/server/notification/VisibilityExtractor.java @@ -15,8 +15,11 @@ */ package com.android.server.notification; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.admin.DevicePolicyManager; import android.content.Context; -import android.service.notification.NotificationListenerService; +import android.os.UserHandle; import android.util.Slog; /** @@ -27,9 +30,11 @@ public class VisibilityExtractor implements NotificationSignalExtractor { private static final boolean DBG = false; private RankingConfig mConfig; + private DevicePolicyManager mDpm; public void initialize(Context ctx, NotificationUsageStats usageStats) { if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); + mDpm = ctx.getSystemService(DevicePolicyManager.class); } public RankingReconsideration process(NotificationRecord record) { @@ -43,7 +48,38 @@ public class VisibilityExtractor implements NotificationSignalExtractor { return null; } - record.setPackageVisibilityOverride(record.getChannel().getLockscreenVisibility()); + int userId = record.getUserId(); + + if (userId == UserHandle.USER_ALL) { + record.setPackageVisibilityOverride(record.getChannel().getLockscreenVisibility()); + } else { + boolean userCanShowNotifications = + mConfig.canShowNotificationsOnLockscreen(userId); + boolean dpmCanShowNotifications = adminAllowsKeyguardFeature(userId, + DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); + boolean channelCanShowNotifications = record.getChannel().getLockscreenVisibility() + != Notification.VISIBILITY_SECRET; + + if (!userCanShowNotifications || !dpmCanShowNotifications + || !channelCanShowNotifications) { + record.setPackageVisibilityOverride(Notification.VISIBILITY_SECRET); + } else { + // notifications are allowed but should they be redacted? + + boolean userCanShowContents = + mConfig.canShowPrivateNotificationsOnLockScreen(userId); + boolean dpmCanShowContents = adminAllowsKeyguardFeature(userId, + DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + boolean channelCanShowContents = record.getChannel().getLockscreenVisibility() + != Notification.VISIBILITY_PRIVATE; + + if (!userCanShowContents || !dpmCanShowContents || !channelCanShowContents) { + record.setPackageVisibilityOverride(Notification.VISIBILITY_PRIVATE); + } else { + record.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE); + } + } + } return null; } @@ -57,4 +93,13 @@ public class VisibilityExtractor implements NotificationSignalExtractor { public void setZenHelper(ZenModeHelper helper) { } + + private boolean adminAllowsKeyguardFeature(int userHandle, int feature) { + if (userHandle == UserHandle.USER_ALL) { + return true; + } + final int dpmFlags = mDpm.getKeyguardDisabledFeatures(null /* admin */, userHandle); + return (dpmFlags & feature) == 0; + } + } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 94f46ba6bc60..5cd22e0bd72d 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -81,6 +81,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; import libcore.io.IoUtils; @@ -1165,7 +1166,7 @@ public class ZenModeHelper { try { parser = resources.getXml(R.xml.default_zen_mode_config); while (parser.next() != XmlPullParser.END_DOCUMENT) { - final ZenModeConfig config = ZenModeConfig.readXml(parser); + final ZenModeConfig config = ZenModeConfig.readXml(XmlUtils.makeTyped(parser)); if (config != null) return config; } } catch (Exception e) { diff --git a/services/core/java/com/android/server/om/TEST_MAPPING b/services/core/java/com/android/server/om/TEST_MAPPING index 6edd76f1810a..e8a2a02e6a8c 100644 --- a/services/core/java/com/android/server/om/TEST_MAPPING +++ b/services/core/java/com/android/server/om/TEST_MAPPING @@ -15,9 +15,6 @@ "name": "OverlayHostTests" }, { - "name": "OverlayRemountedTest" - }, - { "name": "CtsAppSecurityHostTestCases", "options": [ { @@ -25,5 +22,10 @@ } ] } + ], + "presubmit-large": [ + { + "name": "OverlayRemountedTest" + } ] } diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java new file mode 100644 index 000000000000..a17967fcc76e --- /dev/null +++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java @@ -0,0 +1,177 @@ +/* + * 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.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.role.RoleManager; +import android.os.Binder; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.internal.infra.AndroidFuture; +import com.android.internal.util.CollectionUtils; +import com.android.server.FgThread; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Interacts with {@link RoleManager} to provide and manage default apps. + */ +public class DefaultAppProvider { + @NonNull + private final Supplier<RoleManager> mRoleManagerSupplier; + + /** + * Create a new instance of this class + * + * @param roleManagerSupplier the supplier for {@link RoleManager} + */ + public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier) { + mRoleManagerSupplier = roleManagerSupplier; + } + + /** + * Get the package name of the default browser. + * + * @param userId the user ID + * @return the package name of the default browser, or {@code null} if none + */ + @Nullable + public String getDefaultBrowser(@UserIdInt int userId) { + return getRoleHolder(RoleManager.ROLE_BROWSER, userId); + } + + /** + * Set the package name of the default browser. + * + * @param packageName package name of the default browser, or {@code null} to unset + * @param async whether the operation should be asynchronous + * @param userId the user ID + * @return whether the default browser was successfully set. + */ + public boolean setDefaultBrowser(@Nullable String packageName, boolean async, + @UserIdInt int userId) { + if (userId == UserHandle.USER_ALL) { + return false; + } + final RoleManager roleManager = mRoleManagerSupplier.get(); + if (roleManager == null) { + return false; + } + final UserHandle user = UserHandle.of(userId); + final Executor executor = FgThread.getExecutor(); + final AndroidFuture<Void> future = new AndroidFuture<>(); + final Consumer<Boolean> callback = successful -> { + if (successful) { + future.complete(null); + } else { + future.completeExceptionally(new RuntimeException()); + } + }; + final long identity = Binder.clearCallingIdentity(); + try { + if (packageName != null) { + roleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, user, + executor, callback); + } else { + roleManager.clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, user, executor, + callback); + } + if (!async) { + try { + future.get(5, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Slog.e(PackageManagerService.TAG, "Exception while setting default browser: " + + packageName, e); + return false; + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + return true; + } + + /** + * Get the package name of the default dialer. + * + * @param userId the user ID + * @return the package name of the default dialer, or {@code null} if none + */ + @Nullable + public String getDefaultDialer(@NonNull int userId) { + return getRoleHolder(RoleManager.ROLE_DIALER, userId); + } + + /** + * Get the package name of the default home. + * + * @param userId the user ID + * @return the package name of the default home, or {@code null} if none + */ + @Nullable + public String getDefaultHome(@NonNull int userId) { + return getRoleHolder(RoleManager.ROLE_HOME, userId); + } + + /** + * Set the package name of the default home. + * + * @param packageName package name of the default home + * @param userId the user ID + * @param executor the {@link Executor} to execute callback on + * @param callback the callback made after the default home as been updated + * @return whether the default home was set + */ + public boolean setDefaultHome(@NonNull String packageName, @UserIdInt int userId, + @NonNull Executor executor, @NonNull Consumer<Boolean> callback) { + final RoleManager roleManager = mRoleManagerSupplier.get(); + if (roleManager == null) { + return false; + } + final long identity = Binder.clearCallingIdentity(); + try { + roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, packageName, 0, + UserHandle.of(userId), executor, callback); + } finally { + Binder.restoreCallingIdentity(identity); + } + return true; + } + + @Nullable + private String getRoleHolder(@NonNull String roleName, @NonNull int userId) { + final RoleManager roleManager = mRoleManagerSupplier.get(); + if (roleManager == null) { + return null; + } + final long identity = Binder.clearCallingIdentity(); + try { + return CollectionUtils.firstOrNull(roleManager.getRoleHoldersAsUser(roleName, + UserHandle.of(userId))); + } finally { + Binder.restoreCallingIdentity(identity); + } + } +} diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java index 30b1c2c93a45..24a3e520fe1b 100644 --- a/services/core/java/com/android/server/pm/PackageAbiHelper.java +++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java @@ -55,7 +55,7 @@ public interface PackageAbiHelper { * If {@code extractLibs} is true, native libraries are extracted from the app if required. */ Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isUpdatedSystemApp, - String cpuAbiOverride) throws PackageManagerException; + String cpuAbiOverride, File appLib32InstallDir) throws PackageManagerException; /** * Calculates adjusted ABIs for a set of packages belonging to a shared user so that they all diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java index da4ea16d0bfd..71b99bd4ced2 100644 --- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java +++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java @@ -296,7 +296,7 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { @Override public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, - boolean isUpdatedSystemApp, String cpuAbiOverride) + boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir) throws PackageManagerException { // Give ourselves some initial paths; we'll come back for another // pass once we've determined ABI below. @@ -304,7 +304,7 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { String pkgRawSecondaryCpuAbi = AndroidPackageUtils.getRawSecondaryCpuAbi(pkg); final NativeLibraryPaths initialLibraryPaths = deriveNativeLibraryPaths( new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi), - PackageManagerService.sAppLib32InstallDir, pkg.getPath(), + appLib32InstallDir, pkg.getPath(), pkg.getBaseApkPath(), pkg.isSystem(), isUpdatedSystemApp); @@ -452,7 +452,7 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi); return new Pair<>(abis, - deriveNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir, + deriveNativeLibraryPaths(abis, appLib32InstallDir, pkg.getPath(), pkg.getBaseApkPath(), pkg.isSystem(), isUpdatedSystemApp)); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index c10d394d6344..0aebe721b103 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -31,22 +31,16 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE import static android.content.pm.PackageManager.INSTALL_STAGED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.PackageParser.APEX_FILE_EXTENSION; -import static android.content.pm.PackageParser.APK_FILE_EXTENSION; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDONLY; import static android.system.OsConstants.O_WRONLY; import static com.android.internal.util.XmlUtils.readBitmapAttribute; -import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readByteArrayAttribute; -import static com.android.internal.util.XmlUtils.readIntAttribute; -import static com.android.internal.util.XmlUtils.readLongAttribute; import static com.android.internal.util.XmlUtils.readStringAttribute; import static com.android.internal.util.XmlUtils.readUriAttribute; import static com.android.internal.util.XmlUtils.writeBooleanAttribute; import static com.android.internal.util.XmlUtils.writeByteArrayAttribute; -import static com.android.internal.util.XmlUtils.writeIntAttribute; -import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; import static com.android.internal.util.XmlUtils.writeUriAttribute; import static com.android.server.pm.PackageInstallerService.prepareStageDir; @@ -110,6 +104,7 @@ import android.os.ParcelableException; import android.os.Process; import android.os.RemoteException; import android.os.RevocableFileDescriptor; +import android.os.SELinux; import android.os.SystemProperties; import android.os.UserHandle; import android.os.incremental.IStorageHealthListener; @@ -156,7 +151,6 @@ import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayOutputStream; import java.io.File; @@ -483,6 +477,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private IncrementalFileStorages mIncrementalFileStorages; + @GuardedBy("mLock") + private PackageLite mPackageLite; + private static final FileFilter sAddedApkFilter = new FileFilter() { @Override public boolean accept(File file) { @@ -1092,6 +1089,31 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + @Override + public void stageViaHardLink(String path) { + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + throw new SecurityException("link() can only be run by the system"); + } + + try { + final File target = new File(path); + final File source = new File(stageDir, target.getName()); + try { + Os.link(path, source.getAbsolutePath()); + // Grant READ access for APK to be read successfully + Os.chmod(source.getAbsolutePath(), 0644); + } catch (ErrnoException e) { + e.rethrowAsIOException(); + } + if (!SELinux.restorecon(source)) { + throw new IOException("Can't relabel file: " + source); + } + } catch (IOException e) { + throw ExceptionUtils.wrap(e); + } + } + private ParcelFileDescriptor openTargetInternal(String path, int flags, int mode) throws IOException, ErrnoException { // TODO: this should delegate to DCS so the system process avoids @@ -2040,7 +2062,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // TODO(b/136257624): Some logic in this if block probably belongs in // makeInstallParams(). - if (!params.isMultiPackage && !isApexSession()) { + if (!isMultiPackage() && !isApexSession()) { Objects.requireNonNull(mPackageName); Objects.requireNonNull(mSigningDetails); Objects.requireNonNull(mResolvedBaseFile); @@ -2094,12 +2116,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Failed to inherit existing install", e); } } + // For mode inherit existing, it would link/copy existing files to stage dir in the + // above block. Therefore, we need to parse the complete package in stage dir here. + // Besides, PackageLite may be null for staged sessions that don't complete pre-reboot + // verification. + mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0); // TODO: surface more granular state from dexopt mInternalProgress = 0.5f; computeProgressLocked(true); - extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs()); + extractNativeLibraries(mPackageLite, stageDir, params.abiOverride, + mayInheritNativeLibs()); } final IPackageInstallObserver2 localObserver; @@ -2142,7 +2170,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { copiedParams.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; } return mPm.new VerificationParams(user, stageDir, localObserver, copiedParams, - mInstallSource, mInstallerUid, mSigningDetails, sessionId); + mInstallSource, mInstallerUid, mSigningDetails, sessionId, mPackageLite); } private void onVerificationComplete() { @@ -2215,10 +2243,37 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { params.installFlags |= INSTALL_STAGED; } + if (!isMultiPackage() && !isApexSession()) { + synchronized (mLock) { + // This shouldn't be null, but have this code path just in case. + if (mPackageLite == null) { + Slog.wtf(TAG, "Session: " + sessionId + ". Don't have a valid PackageLite."); + } + mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0); + } + } + synchronized (mLock) { return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user, - mSigningDetails, mInstallerUid); + mSigningDetails, mInstallerUid, mPackageLite); + } + } + + @GuardedBy("mLock") + private PackageLite getOrParsePackageLiteLocked(File packageFile, int flags) + throws PackageManagerException { + if (mPackageLite != null) { + return mPackageLite; + } + + final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); + final ParseResult<PackageLite> result = + ApkLiteParseUtils.parsePackageLite(input, packageFile, flags); + if (result.isError()) { + throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, + result.getErrorMessage(), result.getException()); } + return result.getResult(); } private static void maybeRenameFile(File from, File to) throws PackageManagerException { @@ -2366,13 +2421,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - private static String splitNameToFileName(String splitName) { - if (splitName == null) { - return "base"; - } - return "split_" + splitName; - } - /** * Validate install by confirming that all application packages are have * consistent package name, version code, and signing certificates. @@ -2388,6 +2436,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private void validateApkInstallLocked() throws PackageManagerException { ApkLite baseApk = null; + PackageLite packageLite = null; + mPackageLite = null; mPackageName = null; mVersionCode = -1; mSigningDetails = PackageParser.SigningDetails.UNKNOWN; @@ -2431,6 +2481,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Verify that all staged packages are internally consistent final ArraySet<String> stagedSplits = new ArraySet<>(); + final ArrayMap<String, PackageParser.ApkLite> splitApks = new ArrayMap<>(); ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); for (File addedFile : addedFiles) { ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(), @@ -2458,8 +2509,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { assertApkConsistentLocked(String.valueOf(addedFile), apk); // Take this opportunity to enforce uniform naming - final String fileName = splitNameToFileName(apk.splitName); - final String targetName = fileName + APK_FILE_EXTENSION; + final String targetName = ApkLiteParseUtils.splitNameToFileName(apk); if (!FileUtils.isValidExtFilename(targetName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Invalid filename: " + targetName); @@ -2483,6 +2533,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (apk.splitName == null) { mResolvedBaseFile = targetFile; baseApk = apk; + } else { + splitApks.put(apk.splitName, apk); } } @@ -2506,14 +2558,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mVersionCode = pkgInfo.getLongVersionCode(); } if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { - try { - mSigningDetails = ApkSignatureVerifier.unsafeGetCertsWithoutVerification( - pkgInfo.applicationInfo.sourceDir, - PackageParser.SigningDetails.SignatureSchemeVersion.JAR); - } catch (PackageParserException e) { - throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, - "Couldn't obtain signatures from base APK"); - } + mSigningDetails = unsafeGetCertsWithoutVerification( + pkgInfo.applicationInfo.sourceDir); } } @@ -2548,6 +2594,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT, "Missing split for " + mPackageName); } + // For mode full install, we compose package lite for future usage instead of + // re-parsing it again and again. + final ParseResult<PackageLite> pkgLiteResult = + ApkLiteParseUtils.composePackageLiteFromApks(input.reset(), stageDir, baseApk, + splitApks, true); + if (pkgLiteResult.isError()) { + throw new PackageManagerException(pkgLiteResult.getErrorCode(), + pkgLiteResult.getErrorMessage(), pkgLiteResult.getException()); + } + mPackageLite = pkgLiteResult.getResult(); + packageLite = mPackageLite; } else { final ApplicationInfo appInfo = pkgInfo.applicationInfo; ParseResult<PackageLite> pkgLiteResult = ApkLiteParseUtils.parsePackageLite( @@ -2557,22 +2614,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { pkgLiteResult.getErrorMessage(), pkgLiteResult.getException()); } final PackageLite existing = pkgLiteResult.getResult(); - ParseResult<ApkLite> apkLiteResult = ApkLiteParseUtils.parseApkLite(input.reset(), - new File(appInfo.getBaseCodePath()), - PackageParser.PARSE_COLLECT_CERTIFICATES); - if (apkLiteResult.isError()) { - throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, - apkLiteResult.getErrorMessage(), apkLiteResult.getException()); + packageLite = existing; + assertPackageConsistentLocked("Existing", existing.packageName, + existing.getLongVersionCode()); + final PackageParser.SigningDetails signingDetails = + unsafeGetCertsWithoutVerification(existing.baseCodePath); + if (!mSigningDetails.signaturesMatchExactly(signingDetails)) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Existing signatures are inconsistent"); } - final ApkLite existingBase = apkLiteResult.getResult(); - - assertApkConsistentLocked("Existing base", existingBase); // Inherit base if not overridden. if (mResolvedBaseFile == null) { mResolvedBaseFile = new File(appInfo.getBaseCodePath()); inheritFileLocked(mResolvedBaseFile); - baseApk = existingBase; } // Inherit splits if not overridden. @@ -2659,7 +2714,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } // For the case of split required, failed if no splits existed - if (baseApk.isSplitRequired) { + if (packageLite.isSplitRequired) { final int existingSplits = ArrayUtils.size(existing.splitNames); final boolean allSplitsRemoved = (existingSplits == removeSplitList.size()); final boolean onlyBaseFileStaged = (stagedSplits.size() == 1 @@ -2670,7 +2725,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } } - if (baseApk.useEmbeddedDex) { + if (packageLite.useEmbeddedDex) { for (File file : mResolvedStagedFiles) { if (file.getName().endsWith(".apk") && !DexManager.auditUncompressedDexInApk(file.getPath())) { @@ -2683,7 +2738,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID); if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) { - if (!baseApk.debuggable && !baseApk.profilableByShell) { + if (!packageLite.debuggable && !packageLite.profilableByShell) { mIncrementalFileStorages.disableReadLogs(); } } @@ -2838,23 +2893,40 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private void assertApkConsistentLocked(String tag, ApkLite apk) throws PackageManagerException { - if (!mPackageName.equals(apk.packageName)) { + assertPackageConsistentLocked(tag, apk.packageName, apk.getLongVersionCode()); + if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + tag + " signatures are inconsistent"); + } + } + + @GuardedBy("mLock") + private void assertPackageConsistentLocked(String tag, String packageName, + long versionCode) throws PackageManagerException { + if (!mPackageName.equals(packageName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " - + apk.packageName + " inconsistent with " + mPackageName); + + packageName + " inconsistent with " + mPackageName); } - if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) { + if (params.appPackageName != null && !params.appPackageName.equals(packageName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " specified package " + params.appPackageName - + " inconsistent with " + apk.packageName); + + " inconsistent with " + packageName); } - if (mVersionCode != apk.getLongVersionCode()) { + if (mVersionCode != versionCode) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag - + " version code " + apk.versionCode + " inconsistent with " + + " version code " + versionCode + " inconsistent with " + mVersionCode); } - if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) { + } + + private PackageParser.SigningDetails unsafeGetCertsWithoutVerification(String path) + throws PackageManagerException { + try { + return ApkSignatureVerifier.unsafeGetCertsWithoutVerification(path, + PackageParser.SigningDetails.SignatureSchemeVersion.JAR); + } catch (PackageParserException e) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, - tag + " signatures are inconsistent"); + "Couldn't obtain signatures from APK : " + path); } } @@ -2989,8 +3061,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); } - private void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit) + private void extractNativeLibraries(PackageLite packageLite, File packageDir, + String abiOverride, boolean inherit) throws PackageManagerException { + Objects.requireNonNull(packageLite); final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); if (!inherit) { // Start from a clean slate @@ -2999,7 +3073,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { NativeLibraryHelper.Handle handle = null; try { - handle = NativeLibraryHelper.Handle.create(packageDir); + handle = NativeLibraryHelper.Handle.create(packageLite); final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, abiOverride, isIncrementalInstallation()); if (res != INSTALL_SUCCEEDED) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 85659edd1321..a123f369a9ab 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -152,6 +152,7 @@ import android.app.ResourcesManager; import android.app.admin.IDevicePolicyManager; import android.app.admin.SecurityLog; import android.app.backup.IBackupManager; +import android.app.role.RoleManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.BroadcastReceiver; @@ -379,6 +380,8 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; +import com.android.server.pm.permission.LegacyPermissionManagerService; import com.android.server.pm.permission.Permission; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -448,7 +451,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.function.Supplier; /** * Keep track of all those APKs everywhere. @@ -732,8 +734,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000; private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000; - final ServiceThread mHandlerThread; - final Handler mHandler; private final ProcessLoggingHandler mProcessLoggingHandler; @@ -761,12 +761,14 @@ public class PackageManagerService extends IPackageManager.Stub final Installer mInstaller; /** Directory where installed applications are stored */ - private static final File sAppInstallDir = - new File(Environment.getDataDirectory(), "app"); + private final File mAppInstallDir; /** Directory where installed application's 32-bit native libraries are copied. */ @VisibleForTesting - static final File sAppLib32InstallDir = - new File(Environment.getDataDirectory(), "app-lib"); + final File mAppLib32InstallDir; + + private static File getAppLib32InstallDir() { + return new File(Environment.getDataDirectory(), "app-lib"); + } // ---------------------------------------------------------------- @@ -832,6 +834,11 @@ public class PackageManagerService extends IPackageManager.Stub boolean mFirstBoot; + private final boolean mIsEngBuild; + private final boolean mIsUserDebugBuild; + private final String mIncrementalVersion; + + PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy; @GuardedBy("mAvailableFeatures") @@ -866,6 +873,8 @@ public class PackageManagerService extends IPackageManager.Stub private final Injector mInjector; + private final SystemWrapper mSystemWrapper; + /** * The list of all system partitions that may contain packages in ascending order of * specificity (the more generic, the earlier in the list a partition appears). @@ -896,6 +905,10 @@ public class PackageManagerService extends IPackageManager.Stub T produce(Injector injector, PackageManagerService packageManager); } + interface ProducerWithArgument<T, R> { + T produce(Injector injector, PackageManagerService packageManager, R argument); + } + interface ServiceProducer { <T> T produce(Class<T> c); } @@ -924,6 +937,8 @@ public class PackageManagerService extends IPackageManager.Stub private final Object mInstallLock; private final Handler mBackgroundHandler; private final Executor mBackgroundExecutor; + private final List<ScanPartition> mSystemPartitions; + // ----- producers ----- private final Singleton<ComponentResolver> mComponentResolverProducer; @@ -940,13 +955,25 @@ public class PackageManagerService extends IPackageManager.Stub private final Singleton<ViewCompiler> mViewCompilerProducer; private final Singleton<IPermissionManager> mPermissionManagerProducer; private final Singleton<IncrementalManager> mIncrementalManagerProducer; + private final Singleton<DefaultAppProvider> mDefaultAppProviderProducer; + private final Singleton<DisplayMetrics> mDisplayMetricsProducer; + private final Producer<PackageParser2> mScanningCachingPackageParserProducer; + private final Producer<PackageParser2> mScanningPackageParserProducer; + private final Producer<PackageParser2> mPreparingPackageParserProducer; + private final Singleton<PackageInstallerService> mPackageInstallerServiceProducer; + private final ProducerWithArgument<InstantAppResolverConnection, ComponentName> + mInstantAppResolverConnectionProducer; + private final Singleton<LegacyPermissionManagerInternal> + mLegacyPermissionManagerInternalProducer; private final SystemWrapper mSystemWrapper; private final ServiceProducer mGetLocalServiceProducer; private final ServiceProducer mGetSystemServiceProducer; + private final Singleton<ModuleInfoProvider> mModuleInfoProviderProducer; Injector(Context context, Object lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, Handler backgroundHandler, + List<ScanPartition> systemPartitions, Producer<ComponentResolver> componentResolverProducer, Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer, Producer<UserManagerService> userManagerProducer, @@ -961,6 +988,16 @@ public class PackageManagerService extends IPackageManager.Stub Producer<IPermissionManager> permissionManagerProducer, Producer<ViewCompiler> viewCompilerProducer, Producer<IncrementalManager> incrementalManagerProducer, + Producer<DefaultAppProvider> defaultAppProviderProducer, + Producer<DisplayMetrics> displayMetricsProducer, + Producer<PackageParser2> scanningCachingPackageParserProducer, + Producer<PackageParser2> scanningPackageParserProducer, + Producer<PackageParser2> preparingPackageParserProducer, + Producer<PackageInstallerService> packageInstallerServiceProducer, + ProducerWithArgument<InstantAppResolverConnection, ComponentName> + instantAppResolverConnectionProducer, + Producer<ModuleInfoProvider> moduleInfoProviderProducer, + Producer<LegacyPermissionManagerInternal> legacyPermissionManagerInternalProducer, SystemWrapper systemWrapper, ServiceProducer getLocalServiceProducer, ServiceProducer getSystemServiceProducer) { @@ -971,6 +1008,7 @@ public class PackageManagerService extends IPackageManager.Stub mInstallLock = installLock; mBackgroundHandler = backgroundHandler; mBackgroundExecutor = new HandlerExecutor(backgroundHandler); + mSystemPartitions = systemPartitions; mComponentResolverProducer = new Singleton<>(componentResolverProducer); mPermissionManagerServiceProducer = new Singleton<>(permissionManagerServiceProducer); mUserManagerProducer = new Singleton<>(userManagerProducer); @@ -985,6 +1023,16 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManagerProducer = new Singleton<>(permissionManagerProducer); mViewCompilerProducer = new Singleton<>(viewCompilerProducer); mIncrementalManagerProducer = new Singleton<>(incrementalManagerProducer); + mDefaultAppProviderProducer = new Singleton<>(defaultAppProviderProducer); + mDisplayMetricsProducer = new Singleton<>(displayMetricsProducer); + mScanningCachingPackageParserProducer = scanningCachingPackageParserProducer; + mScanningPackageParserProducer = scanningPackageParserProducer; + mPreparingPackageParserProducer = preparingPackageParserProducer; + mPackageInstallerServiceProducer = new Singleton<>(packageInstallerServiceProducer); + mInstantAppResolverConnectionProducer = instantAppResolverConnectionProducer; + mModuleInfoProviderProducer = new Singleton<>(moduleInfoProviderProducer); + mLegacyPermissionManagerInternalProducer = new Singleton<>( + legacyPermissionManagerInternalProducer); mSystemWrapper = systemWrapper; mGetLocalServiceProducer = getLocalServiceProducer; mGetSystemServiceProducer = getSystemServiceProducer; @@ -1010,6 +1058,10 @@ public class PackageManagerService extends IPackageManager.Stub return mInstallLock; } + public List<ScanPartition> getSystemPartitions() { + return mSystemPartitions; + } + public UserManagerService getUserManagerService() { return mUserManagerProducer.get(this, mPackageManager); } @@ -1082,6 +1134,10 @@ public class PackageManagerService extends IPackageManager.Stub return mBackgroundExecutor; } + public DisplayMetrics getDisplayMetrics() { + return mDisplayMetricsProducer.get(this, mPackageManager); + } + public <T> T getLocalService(Class<T> c) { return mGetLocalServiceProducer.produce(c); } @@ -1097,53 +1153,67 @@ public class PackageManagerService extends IPackageManager.Stub public IncrementalManager getIncrementalManager() { return mIncrementalManagerProducer.get(this, mPackageManager); } - } - /** Provides an abstraction to static access to system state. */ - public interface SystemWrapper { - /** @see SystemProperties#get(String) */ - String getProperty(String key); - /** @see SystemProperties#getInt(String, int) */ - int getPropertyInt(String key, int defValue); - /** @see SystemProperties#getBoolean(String, boolean) */ - boolean getPropertyBoolean(String key, boolean defValue); - /** @see SystemProperties#digestOf(String...) */ - String digestOfProperties(@NonNull String... keys); - /** @see SystemProperties#set(String, String) */ - void setProperty(String key, String value); - /** @see Build.VERSION#SDK_INT */ - int getSdkInt(); - } + public DefaultAppProvider getDefaultAppProvider() { + return mDefaultAppProviderProducer.get(this, mPackageManager); + } - private static class DefaultSystemWrapper implements SystemWrapper { - @Override - public String getProperty(String key) { - return SystemProperties.get(key); + public PackageParser2 getScanningCachingPackageParser() { + return mScanningCachingPackageParserProducer.produce(this, mPackageManager); + } + public PackageParser2 getScanningPackageParser() { + return mScanningPackageParserProducer.produce(this, mPackageManager); + } + public PackageParser2 getPreparingPackageParser() { + return mPreparingPackageParserProducer.produce(this, mPackageManager); } - @Override - public int getPropertyInt(String key, int defValue) { - return SystemProperties.getInt(key, defValue); + public PackageInstallerService getPackageInstallerService() { + return mPackageInstallerServiceProducer.get(this, mPackageManager); } - @Override - public boolean getPropertyBoolean(String key, boolean defValue) { - return SystemProperties.getBoolean(key, defValue); + public InstantAppResolverConnection getInstantAppResolverConnection( + ComponentName instantAppResolverComponent) { + return mInstantAppResolverConnectionProducer.produce( + this, mPackageManager, instantAppResolverComponent); } - @Override - public String digestOfProperties(String... keys) { - return SystemProperties.digestOf(keys); + public ModuleInfoProvider getModuleInfoProvider() { + return mModuleInfoProviderProducer.get(this, mPackageManager); } + public LegacyPermissionManagerInternal getLegacyPermissionManagerInternal() { + return mLegacyPermissionManagerInternalProducer.get(this, mPackageManager); + } + } + + /** Provides an abstraction to static access to system state. */ + public interface SystemWrapper { + void disablePackageCaches(); + void enablePackageCaches(); + } + + private static class DefaultSystemWrapper implements SystemWrapper { + @Override - public void setProperty(String key, String value) { - SystemProperties.set(key, value); + public void disablePackageCaches() { + // disable all package caches that shouldn't apply within system server + PackageManager.disableApplicationInfoCache(); + PackageManager.disablePackageInfoCache(); + ApplicationPackageManager.invalidateGetPackagesForUidCache(); + ApplicationPackageManager.disableGetPackagesForUidCache(); + ApplicationPackageManager.invalidateHasSystemFeatureCache(); + + // Avoid invalidation-thrashing by preventing cache invalidations from causing property + // writes if the cache isn't enabled yet. We re-enable writes later when we're + // done initializing. + PackageManager.corkPackageInfoCache(); } @Override - public int getSdkInt() { - return Build.VERSION.SDK_INT; + public void enablePackageCaches() { + // Uncork cache invalidations and allow clients to cache package information. + PackageManager.uncorkPackageInfoCache(); } } @@ -1154,13 +1224,13 @@ public class PackageManagerService extends IPackageManager.Stub public ArtManagerService artManagerService; public @Nullable String configuratorPackage; public int defParseFlags; + public DefaultAppProvider defaultAppProvider; public DexManager dexManager; public List<ScanPartition> dirsToScanAsSystem; public @Nullable String documenterPackage; public boolean factoryTest; public ArrayMap<String, FeatureInfo> availableFeatures; public Handler handler; - public ServiceThread handlerThread; public @Nullable String incidentReportApproverPackage; public IncrementalManager incrementalManager; public PackageInstallerService installerService; @@ -1173,6 +1243,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean isPreNupgrade; public boolean isPreQupgrade; public boolean isUpgrade; + public LegacyPermissionManagerInternal legacyPermissionManagerInternal; public DisplayMetrics Metrics; public ModuleInfoProvider moduleInfoProvider; public MoveCallbacks moveCallbacks; @@ -1206,6 +1277,13 @@ public class PackageManagerService extends IPackageManager.Stub public ArrayMap<String, AndroidPackage> packages; public boolean enableFreeCacheV2; public int sdkVersion; + public SystemWrapper systemWrapper; + public File appInstallDir; + public File appLib32InstallDir; + public boolean isEngBuild; + public boolean isUserDebugBuild; + public int sdkInt = Build.VERSION.SDK_INT; + public String incrementalVersion = Build.VERSION.INCREMENTAL; } private final AppsFilter mAppsFilter; @@ -1307,6 +1385,10 @@ public class PackageManagerService extends IPackageManager.Stub private final IncrementalManager mIncrementalManager; + private final DefaultAppProvider mDefaultAppProvider; + + private final LegacyPermissionManagerInternal mLegacyPermissionManager; + private final PackageProperty mPackageProperty = new PackageProperty(); private static class IFVerificationParams { @@ -1738,6 +1820,7 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mOverlayConfigSignaturePackage; final @Nullable String mRecentsPackage; + @GuardedBy("mLock") private final PackageUsage mPackageUsage = new PackageUsage(); private final CompilerStats mCompilerStats = new CompilerStats(); @@ -1802,7 +1885,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int index = 0; i < size && index < numComponents; index++) { packages[i] = componentsToBroadcast.keyAt(index); components[i] = componentsToBroadcast.valueAt(index); - final PackageSetting ps = mSettings.mPackages.get(packages[i]); + final PackageSetting ps = mSettings.getPackageLPr(packages[i]); uids[i] = (ps != null) ? UserHandle.getUid(packageUserId, ps.appId) : -1; @@ -2210,22 +2293,27 @@ public class PackageManagerService extends IPackageManager.Stub res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/); } + final PermissionManagerServiceInternal.PackageInstalledParams.Builder + permissionParamsBuilder = + new PermissionManagerServiceInternal.PackageInstalledParams.Builder(); final List<String> grantedPermissionsList; if (grantPermissions) { if (grantedPermissions != null) { - grantedPermissionsList = Arrays.asList(grantedPermissions); + permissionParamsBuilder.setGrantedPermissions(Arrays.asList( + grantedPermissions)); } else { - grantedPermissionsList = res.pkg.getRequestedPermissions(); + permissionParamsBuilder.setGrantedPermissions( + res.pkg.getRequestedPermissions()); } - } else { - grantedPermissionsList = Collections.emptyList(); } - if (allowlistedRestrictedPermissions == null) { - allowlistedRestrictedPermissions = Collections.emptyList(); + if (allowlistedRestrictedPermissions != null) { + permissionParamsBuilder.setAllowlistedRestrictedPermissions( + allowlistedRestrictedPermissions); } + permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode); for (final int userId : res.newUsers) { - mPermissionManager.onPackageInstalled(res.pkg, grantedPermissionsList, - allowlistedRestrictedPermissions, autoRevokePermissionsMode, userId); + mPermissionManager.onPackageInstalled(res.pkg, permissionParamsBuilder.build(), + userId); } final String installerPackageName = @@ -2304,7 +2392,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { newBroadcastAllowList = mAppsFilter.getVisibilityAllowList( getPackageSettingInternal(res.name, Process.SYSTEM_UID), - updateUserIds, mSettings.mPackages); + updateUserIds, mSettings.getPackagesLocked()); } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, @@ -2741,6 +2829,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector injector = new Injector( context, lock, installer, installLock, new PackageAbiHelperImpl(), backgroundHandler, + SYSTEM_PARTITIONS, (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock), (i, pm) -> PermissionManagerService.create(context), (i, pm) -> new UserManagerService(context, pm, @@ -2762,12 +2851,38 @@ public class PackageManagerService extends IPackageManager.Stub (i, pm) -> (IPermissionManager) ServiceManager.getService("permissionmgr"), (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()), (i, pm) -> (IncrementalManager) - pm.mContext.getSystemService(Context.INCREMENTAL_SERVICE), + i.getContext().getSystemService(Context.INCREMENTAL_SERVICE), + (i, pm) -> new DefaultAppProvider(() -> context.getSystemService( + RoleManager.class)), + (i, pm) -> new DisplayMetrics(), + (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore, + i.getDisplayMetrics(), pm.mCacheDir, + pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */, + (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore, + i.getDisplayMetrics(), null, + pm.mPackageParserCallback) /* scanningPackageParserProducer */, + (i, pm) -> new PackageParser2(pm.mSeparateProcesses, false, i.getDisplayMetrics(), + null, pm.mPackageParserCallback) /* preparingPackageParserProducer */, + // Prepare a supplier of package parser for the staging manager to parse apex file + // during the staging installation. + (i, pm) -> new PackageInstallerService( + i.getContext(), pm, i::getScanningPackageParser), + (i, pm, cn) -> new InstantAppResolverConnection( + i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE), + (i, pm) -> new ModuleInfoProvider(i.getContext(), pm), + (i, pm) -> LegacyPermissionManagerService.create(i.getContext()), new DefaultSystemWrapper(), LocalServices::getService, context::getSystemService); - PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest); + + if (Build.VERSION.SDK_INT <= 0) { + Slog.w(TAG, "**** ro.build.version.sdk not set!"); + } + + PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest, + Build.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT, + Build.VERSION.INCREMENTAL); t.traceEnd(); // "create package manager" final CompatChange.ChangeListener selinuxChangeListener = packageName -> { @@ -2827,11 +2942,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private static void getDefaultDisplayMetrics( - DisplayManager displayManager, DisplayMetrics metrics) { - displayManager.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(metrics); - } - /** * Requests that files preopted on a secondary system partition be copied to the data partition * if possible. Note that the actual copying of the files is accomplished by init for security @@ -2841,14 +2951,13 @@ public class PackageManagerService extends IPackageManager.Stub private static void requestCopyPreoptedFiles(Injector injector) { final int WAIT_TIME_MS = 100; final String CP_PREOPT_PROPERTY = "sys.cppreopt"; - if (injector.getSystemWrapper().getPropertyInt("ro.cp_system_other_odex", 0) == 1) { - injector.getSystemWrapper().setProperty(CP_PREOPT_PROPERTY, "requested"); + if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) { + SystemProperties.set(CP_PREOPT_PROPERTY, "requested"); // We will wait for up to 100 seconds. final long timeStart = SystemClock.uptimeMillis(); final long timeEnd = timeStart + 100 * 1000; long timeNow = timeStart; - while (!injector.getSystemWrapper() - .getProperty(CP_PREOPT_PROPERTY).equals("finished")) { + while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) { try { Thread.sleep(WAIT_TIME_MS); } catch (InterruptedException e) { @@ -2856,7 +2965,7 @@ public class PackageManagerService extends IPackageManager.Stub } timeNow = SystemClock.uptimeMillis(); if (timeNow > timeEnd) { - injector.getSystemWrapper().setProperty(CP_PREOPT_PROPERTY, "timed-out"); + SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out"); Slog.wtf(TAG, "cppreopt did not finish!"); break; } @@ -2933,16 +3042,16 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager = injector.getPermissionManagerServiceInternal(); mSettings = injector.getSettings(); mUserManager = injector.getUserManagerService(); - mApexManager = testParams.apexManager; mArtManagerService = testParams.artManagerService; mAvailableFeatures = testParams.availableFeatures; mDefParseFlags = testParams.defParseFlags; + mDefaultAppProvider = testParams.defaultAppProvider; + mLegacyPermissionManager = testParams.legacyPermissionManagerInternal; mDexManager = testParams.dexManager; mDirsToScanAsSystem = testParams.dirsToScanAsSystem; mFactoryTest = testParams.factoryTest; mHandler = testParams.handler; - mHandlerThread = testParams.handlerThread; mIncrementalManager = testParams.incrementalManager; mInstallerService = testParams.installerService; mInstantAppRegistry = testParams.instantAppRegistry; @@ -2987,49 +3096,46 @@ public class PackageManagerService extends IPackageManager.Stub mServicesExtensionPackageName = testParams.servicesExtensionPackageName; mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName; mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage; - mResolveComponentName = testParams.resolveComponentName; mPackages.putAll(testParams.packages); mEnableFreeCacheV2 = testParams.enableFreeCacheV2; mSdkVersion = testParams.sdkVersion; - } - - public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) { - PackageManager.disableApplicationInfoCache(); - PackageManager.disablePackageInfoCache(); - ApplicationPackageManager.invalidateGetPackagesForUidCache(); - ApplicationPackageManager.disableGetPackagesForUidCache(); - ApplicationPackageManager.invalidateHasSystemFeatureCache(); - - // Avoid invalidation-thrashing by preventing cache invalidations from causing property - // writes if the cache isn't enabled yet. We re-enable writes later when we're - // done initializing. - PackageManager.corkPackageInfoCache(); + mSystemWrapper = testParams.systemWrapper; + mAppInstallDir = testParams.appInstallDir; + mAppLib32InstallDir = testParams.appLib32InstallDir; + mIsEngBuild = testParams.isEngBuild; + mIsUserDebugBuild = testParams.isUserDebugBuild; + mIncrementalVersion = testParams.incrementalVersion; + } + + public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest, + final String buildFingerprint, final boolean isEngBuild, + final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) { + mIsEngBuild = isEngBuild; + mIsUserDebugBuild = isUserDebugBuild; + mSdkVersion = sdkVersion; + mIncrementalVersion = incrementalVersion; + mInjector = injector; + mInjector.getSystemWrapper().disablePackageCaches(); final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", Trace.TRACE_TAG_PACKAGE_MANAGER); mPendingBroadcasts = new PendingPackageBroadcasts(); - mInjector = injector; mInjector.bootstrap(this); mLock = injector.getLock(); mInstallLock = injector.getInstallLock(); LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); - mSdkVersion = injector.getSystemWrapper().getSdkInt(); - - if (mSdkVersion <= 0) { - Slog.w(TAG, "**** ro.build.version.sdk not set!"); - } + mSystemWrapper = injector.getSystemWrapper(); mContext = injector.getContext(); mFactoryTest = factoryTest; mOnlyCore = onlyCore; - mMetrics = new DisplayMetrics(); + mMetrics = injector.getDisplayMetrics(); mInstaller = injector.getInstaller(); - mEnableFreeCacheV2 = - injector.getSystemWrapper().getPropertyBoolean("fw.free_cache_v2", true); + mEnableFreeCacheV2 = SystemProperties.getBoolean("fw.free_cache_v2", true); // Create sub-components that provide services / data. Order here is important. t.traceBegin("createSubComponents"); @@ -3045,6 +3151,8 @@ public class PackageManagerService extends IPackageManager.Stub mSettings = injector.getSettings(); mPermissionManagerService = injector.getPermissionManagerService(); mIncrementalManager = mInjector.getIncrementalManager(); + mDefaultAppProvider = mInjector.getDefaultAppProvider(); + mLegacyPermissionManager = mInjector.getLegacyPermissionManagerInternal(); PlatformCompat platformCompat = mInjector.getCompatibility(); mPackageParserCallback = new PackageParser2.Callback() { @Override @@ -3080,8 +3188,8 @@ public class PackageManagerService extends IPackageManager.Stub ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); t.traceEnd(); - String separateProcesses = - injector.getSystemWrapper().getProperty("debug.separate_processes"); + String separateProcesses = SystemProperties.get("debug.separate_processes"); + if (separateProcesses != null && separateProcesses.length() > 0) { if ("*".equals(separateProcesses)) { mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES; @@ -3104,7 +3212,8 @@ public class PackageManagerService extends IPackageManager.Stub mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper()); mViewCompiler = injector.getViewCompiler(); - getDefaultDisplayMetrics(mInjector.getSystemService(DisplayManager.class), mMetrics); + mContext.getSystemService(DisplayManager.class) + .getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics); t.traceBegin("get system config"); SystemConfig systemConfig = injector.getSystemConfig(); @@ -3126,18 +3235,21 @@ public class PackageManagerService extends IPackageManager.Stub } mDirsToScanAsSystem = new ArrayList<>(); - mDirsToScanAsSystem.addAll(SYSTEM_PARTITIONS); + mDirsToScanAsSystem.addAll(injector.getSystemPartitions()); mDirsToScanAsSystem.addAll(scanPartitions); Slog.d(TAG, "Directories scanned as system partitions: " + mDirsToScanAsSystem); + mAppInstallDir = new File(Environment.getDataDirectory(), "app"); + mAppLib32InstallDir = getAppLib32InstallDir(); + // CHECKSTYLE:OFF IndentationCheck synchronized (mInstallLock) { // writer synchronized (mLock) { - mHandlerThread = new ServiceThread(TAG, + HandlerThread handlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); - mHandlerThread.start(); - mHandler = new PackageHandler(mHandlerThread.getLooper()); + handlerThread.start(); + mHandler = new PackageHandler(handlerThread.getLooper()); mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager); @@ -3182,12 +3294,13 @@ public class PackageManagerService extends IPackageManager.Stub // Clean up orphaned packages for which the code path doesn't exist // and they are an update to a system app - caused by bug/32321269 - final int packageSettingCount = mSettings.mPackages.size(); + final ArrayMap<String, PackageSetting> packageSettings = mSettings.getPackagesLocked(); + final int packageSettingCount = packageSettings.size(); for (int i = packageSettingCount - 1; i >= 0; i--) { - PackageSetting ps = mSettings.mPackages.valueAt(i); + PackageSetting ps = packageSettings.valueAt(i); if (!isExternal(ps) && (ps.getPath() == null || !ps.getPath().exists()) && mSettings.getDisabledSystemPkgLPr(ps.name) != null) { - mSettings.mPackages.removeAt(i); + packageSettings.removeAt(i); mSettings.enableSystemPackageLPw(ps.name); } } @@ -3222,9 +3335,10 @@ public class PackageManagerService extends IPackageManager.Stub File frameworkDir = new File(Environment.getRootDirectory(), "framework"); final VersionInfo ver = mSettings.getInternalVersion(); - mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); + mIsUpgrade = + !buildFingerprint.equals(ver.fingerprint); if (mIsUpgrade) { - logCriticalInfo(Log.INFO, + PackageManagerServiceUtils.logCriticalInfo(Log.INFO, "Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT); } @@ -3242,13 +3356,13 @@ public class PackageManagerService extends IPackageManager.Stub // Save the names of pre-existing packages prior to scanning, so we can determine // which system packages are completely new due to an upgrade. if (isDeviceUpgrading()) { - mExistingPackages = new ArraySet<>(mSettings.mPackages.size()); - for (PackageSetting ps : mSettings.mPackages.values()) { + mExistingPackages = new ArraySet<>(packageSettings.size()); + for (PackageSetting ps : packageSettings.values()) { mExistingPackages.add(ps.name); } } - mCacheDir = preparePackageParserCache(injector); + mCacheDir = preparePackageParserCache(mIsEngBuild); // Set flag to monitor and not change apk file paths when // scanning install directories. @@ -3261,8 +3375,7 @@ public class PackageManagerService extends IPackageManager.Stub final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM; - PackageParser2 packageParser = new PackageParser2(mSeparateProcesses, mOnlyCore, - mMetrics, mCacheDir, mPackageParserCallback); + PackageParser2 packageParser = injector.getScanningCachingPackageParser(); ExecutorService executorService = ParallelPackageParser.makeExecutorService(); // Prepare apex package info before scanning APKs, these information are needed when @@ -3312,6 +3425,7 @@ public class PackageManagerService extends IPackageManager.Stub // Stub packages must either be replaced with full versions in the /data // partition or be disabled. final List<String> stubSystemApps = new ArrayList<>(); + final int[] userIds = mUserManager.getUserIds(); if (!mOnlyCore) { // do this first before mucking with mPackages for the "expecting better" case final int numPackages = mPackages.size(); @@ -3324,8 +3438,8 @@ public class PackageManagerService extends IPackageManager.Stub // Iterates PackageSettings in reversed order because the item could be removed // during the iteration. - for (int index = mSettings.mPackages.size() - 1; index >= 0; index--) { - final PackageSetting ps = mSettings.mPackages.valueAt(index); + for (int index = packageSettings.size() - 1; index >= 0; index--) { + final PackageSetting ps = packageSettings.valueAt(index); /* * If this is not a system app, it can't be a @@ -3362,15 +3476,9 @@ public class PackageManagerService extends IPackageManager.Stub } if (!mSettings.isDisabledSystemPackageLPr(ps.name)) { - mSettings.mPackages.removeAt(index); logCriticalInfo(Log.WARN, "System package " + ps.name + " no longer exists; it's data will be wiped"); - - // Assume package is truly gone and wipe residual permissions. - mPermissionManager.updatePermissions(ps.name, null); - - // Actual deletion of code and data will be handled by later - // reconciliation step + removePackageDataLIF(ps, userIds, null, 0, false); } else { // we still have a disabled system package, but, it still might have // been removed. check the code path still exists and check there's @@ -3412,7 +3520,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!mOnlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); - scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0, + scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0, packageParser, executorService); } @@ -3429,7 +3537,6 @@ public class PackageManagerService extends IPackageManager.Stub // Remove disable package settings for updated system apps that were // removed via an OTA. If the update is no longer present, remove the // app completely. Otherwise, revoke their system privileges. - final int[] userIds = mUserManager.getUserIds(); for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) { final String packageName = possiblyDeletedUpdatedSystemApps.get(i); final AndroidPackage pkg = mPackages.get(packageName); @@ -3470,7 +3577,7 @@ public class PackageManagerService extends IPackageManager.Stub // previously scanned and known to the system], but, we don't have // a package [ie. there was an error scanning it from the /data // partition], completely remove the package data. - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && mPackages.get(packageName) == null) { removePackageDataLIF(ps, userIds, null, 0, false); @@ -3596,7 +3703,7 @@ public class PackageManagerService extends IPackageManager.Stub // Now that we know all the packages we are keeping, // read and update their last usage times. - mPackageUsage.read(mSettings.mPackages); + mPackageUsage.read(packageSettings); mCompilerStats.read(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, @@ -3689,8 +3796,8 @@ public class PackageManagerService extends IPackageManager.Stub // profile compilation (without waiting to collect a fresh set of profiles). if (mIsUpgrade && !mOnlyCore) { Slog.i(TAG, "Build fingerprint changed; clearing code caches"); - for (int i = 0; i < mSettings.mPackages.size(); i++) { - final PackageSetting ps = mSettings.mPackages.valueAt(i); + for (int i = 0; i < packageSettings.size(); i++) { + final PackageSetting ps = packageSettings.valueAt(i); if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) { // No apps are running this early, so no need to freeze clearAppDataLIF(ps.pkg, UserHandle.USER_ALL, @@ -3706,9 +3813,9 @@ public class PackageManagerService extends IPackageManager.Stub // their icons in launcher. if (!mOnlyCore && mIsPreQUpgrade) { Slog.i(TAG, "Whitelisting all existing apps to hide their icons"); - int size = mSettings.mPackages.size(); + int size = packageSettings.size(); for (int i = 0; i < size; i++) { - final PackageSetting ps = mSettings.mPackages.valueAt(i); + final PackageSetting ps = packageSettings.valueAt(i); if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { continue; } @@ -3764,7 +3871,6 @@ public class PackageManagerService extends IPackageManager.Stub UserHandle.USER_SYSTEM).getLongVersionCode()); // Initialize InstantAppRegistry's Instant App list for all users. - final int[] userIds = UserManagerService.getInstance().getUserIds(); for (AndroidPackage pkg : mPackages.values()) { if (pkg.isSystem()) { continue; @@ -3778,23 +3884,16 @@ public class PackageManagerService extends IPackageManager.Stub } } - // Prepare a supplier of package parser for the staging manager to parse apex file - // during the staging installation. - final Supplier<PackageParser2> apexParserSupplier = () -> new PackageParser2( - mSeparateProcesses, mOnlyCore, mMetrics, null /* cacheDir */, - mPackageParserCallback); - mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier); - final Pair<ComponentName, String> instantAppResolverComponent = - getInstantAppResolverLPr(); + mInstallerService = mInjector.getPackageInstallerService(); + final ComponentName instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { if (DEBUG_INSTANT) { Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent); } - mInstantAppResolverConnection = new InstantAppResolverConnection( - mContext, instantAppResolverComponent.first, - instantAppResolverComponent.second); + mInstantAppResolverConnection = + mInjector.getInstantAppResolverConnection(instantAppResolverComponent); mInstantAppResolverSettingsComponent = - getInstantAppResolverSettingsLPr(instantAppResolverComponent.first); + getInstantAppResolverSettingsLPr(instantAppResolverComponent); } else { mInstantAppResolverConnection = null; mInstantAppResolverSettingsComponent = null; @@ -3824,10 +3923,8 @@ public class PackageManagerService extends IPackageManager.Stub } // synchronized (mInstallLock) // CHECKSTYLE:ON IndentationCheck - mModuleInfoProvider = new ModuleInfoProvider(mContext, this); - - // Uncork cache invalidations and allow clients to cache package information. - PackageManager.uncorkPackageInfoCache(); + mModuleInfoProvider = mInjector.getModuleInfoProvider(); + mInjector.getSystemWrapper().enablePackageCaches(); // Now after opening every single application zip, make sure they // are all flushed. Not really needed, but keeps things nice and @@ -3875,7 +3972,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } // skip if the package has been disabled by the user - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM); if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { @@ -3900,7 +3997,7 @@ public class PackageManagerService extends IPackageManager.Stub // disable any stub still left; these failed to install the full application for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { final String pkgName = systemStubPackageNames.get(i); - final PackageSetting ps = mSettings.mPackages.get(pkgName); + final PackageSetting ps = mSettings.getPackageLPr(pkgName); ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName); @@ -3930,7 +4027,12 @@ public class PackageManagerService extends IPackageManager.Stub } catch (PackageManagerException e) { Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e); } - mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); + final int[] userIds = mUserManager.getUserIds(); + for (final int userId : userIds) { + mPermissionManager.onPackageInstalled(pkg, + PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, + userId); + } writeSettingsLPrTEMP(); } } catch (PackageManagerException e) { @@ -3952,7 +4054,7 @@ public class PackageManagerService extends IPackageManager.Stub } finally { // Disable the package; the stub by itself is not runnable synchronized (mLock) { - final PackageSetting stubPs = mSettings.mPackages.get( + final PackageSetting stubPs = mSettings.getPackageLPr( stubPkg.getPackageName()); if (stubPs != null) { stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, @@ -4072,19 +4174,18 @@ public class PackageManagerService extends IPackageManager.Stub setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr()); } - private static @Nullable File preparePackageParserCache(Injector injector) { + private @Nullable File preparePackageParserCache(boolean forEngBuild) { if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) { if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) { return null; } // Disable package parsing on eng builds to allow for faster incremental development. - if (Build.IS_ENG) { + if (forEngBuild) { return null; } - if (injector.getSystemWrapper() - .getPropertyBoolean("pm.boot.disable_package_cache", false)) { + if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) { Slog.i(TAG, "Disabling package parser cache due to system property."); return null; } @@ -4100,8 +4201,7 @@ public class PackageManagerService extends IPackageManager.Stub // identify cached items. In particular, changing the value of certain // feature flags should cause us to invalidate any caches. final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug" - : injector.getSystemWrapper().digestOfProperties( - "ro.build.fingerprint"); + : SystemProperties.digestOf("ro.build.fingerprint"); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { @@ -4130,14 +4230,15 @@ public class PackageManagerService extends IPackageManager.Stub // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build // that starts with "eng." to signify that this is an engineering build and not // destined for release. - if (Build.IS_USERDEBUG && Build.VERSION.INCREMENTAL.startsWith("eng.")) { + if (mIsUserDebugBuild && mIncrementalVersion.startsWith("eng.")) { Slog.w(TAG, "Wiping cache directory because the system partition changed."); // Heuristic: If the /system directory has been modified recently due to an "adb sync" // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable // in general and should not be used for production changes. In this specific case, // we know that they will work. - File frameworkDir = new File(Environment.getRootDirectory(), "framework"); + File frameworkDir = + new File(Environment.getRootDirectory(), "framework"); if (cacheDir.lastModified() < frameworkDir.lastModified()) { FileUtils.deleteContents(cacheBaseDir); cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); @@ -4163,7 +4264,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean isDeviceUpgrading() { // allow instant applications // The system property allows testing ota flow when upgraded to the same image. - return mIsUpgrade || mInjector.getSystemWrapper().getPropertyBoolean( + return mIsUpgrade || SystemProperties.getBoolean( "persist.pm.mock-upgrade", false /* default */); } @@ -4297,15 +4398,15 @@ public class PackageManagerService extends IPackageManager.Stub return null; } synchronized (mLock) { - final Pair<ComponentName, String> instantAppResolver = getInstantAppResolverLPr(); + final ComponentName instantAppResolver = getInstantAppResolverLPr(); if (instantAppResolver == null) { return null; } - return instantAppResolver.first; + return instantAppResolver; } } - private @Nullable Pair<ComponentName, String> getInstantAppResolverLPr() { + private @Nullable ComponentName getInstantAppResolverLPr() { final String[] packageArray = mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage); if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) { @@ -4320,8 +4421,7 @@ public class PackageManagerService extends IPackageManager.Stub MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); - String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE; - final Intent resolverIntent = new Intent(actionName); + final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE); List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null, resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); final int N = resolvers.size(); @@ -4353,7 +4453,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.v(TAG, "Ephemeral resolver found;" + " pkg: " + packageName + ", info:" + info); } - return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName); + return new ComponentName(packageName, info.serviceInfo.name); } if (DEBUG_INSTANT) { Slog.v(TAG, "Ephemeral resolver NOT found"); @@ -4363,7 +4463,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") private @Nullable ActivityInfo getInstantAppInstallerLPr() { - String[] orderedActions = Build.IS_ENG + String[] orderedActions = mIsEngBuild ? new String[]{ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST", Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE} @@ -4374,7 +4474,7 @@ public class PackageManagerService extends IPackageManager.Stub MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | Intent.FLAG_IGNORE_EPHEMERAL - | (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0); + | (mIsEngBuild ? 0 : MATCH_SYSTEM_ONLY); final Intent intent = new Intent(); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); @@ -4394,8 +4494,9 @@ public class PackageManagerService extends IPackageManager.Stub Iterator<ResolveInfo> iter = matches.iterator(); while (iter.hasNext()) { final ResolveInfo rInfo = iter.next(); - if (checkPermission(Manifest.permission.INSTALL_PACKAGES, - rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || Build.IS_ENG) { + if (checkPermission( + Manifest.permission.INSTALL_PACKAGES, + rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || mIsEngBuild) { continue; } iter.remove(); @@ -4623,7 +4724,7 @@ public class PackageManagerService extends IPackageManager.Stub enforceCrossUserPermission(callingUid, userId, false, false, "checkPackageStartable"); final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId); synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { throw new SecurityException("Package " + packageName + " was not found!"); } @@ -4739,7 +4840,7 @@ public class PackageManagerService extends IPackageManager.Stub return generatePackageInfo(ps, flags, userId); } if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) return null; if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; @@ -4947,7 +5048,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { - final PackageSetting ps = mSettings.mPackages.get(names[i]); + final PackageSetting ps = mSettings.getPackageLPr(names[i]); boolean translateName = false; if (ps != null && ps.realName != null) { final boolean targetIsInstantApp = ps.getInstantApp(callingUserId); @@ -4977,7 +5078,7 @@ public class PackageManagerService extends IPackageManager.Stub final String cur = mSettings.getRenamedPackageLPr(names[i]); boolean translateName = false; if (cur != null) { - final PackageSetting ps = mSettings.mPackages.get(names[i]); + final PackageSetting ps = mSettings.getPackageLPr(names[i]); final boolean targetIsInstantApp = ps != null && ps.getInstantApp(callingUserId); translateName = !targetIsInstantApp @@ -5013,7 +5114,7 @@ public class PackageManagerService extends IPackageManager.Stub return UserHandle.getUid(userId, p.getUid()); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && ps.isMatch(flags) && !shouldFilterApplicationLocked(ps, callingUid, userId)) { return UserHandle.getUid(userId, ps.appId); @@ -5045,7 +5146,7 @@ public class PackageManagerService extends IPackageManager.Stub return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId)); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && ps.isMatch(flags) && !shouldFilterApplicationLocked(ps, callingUid, userId)) { return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId)); @@ -5071,7 +5172,7 @@ public class PackageManagerService extends IPackageManager.Stub private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; @@ -5129,7 +5230,7 @@ public class PackageManagerService extends IPackageManager.Stub TAG, "getApplicationInfo " + packageName + ": " + p); if (p != null) { - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) return null; if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; @@ -5598,7 +5699,7 @@ public class PackageManagerService extends IPackageManager.Stub if (a == null) { return false; } - PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(component.getPackageName()); if (ps == null) { return false; } @@ -5638,7 +5739,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) { - PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(component.getPackageName()); if (ps == null) return null; if (shouldFilterApplicationLocked( ps, callingUid, component, TYPE_RECEIVER, userId)) { @@ -5798,9 +5899,9 @@ public class PackageManagerService extends IPackageManager.Stub private List<VersionedPackage> getPackagesUsingSharedLibraryLPr( SharedLibraryInfo libInfo, int flags, int userId) { List<VersionedPackage> versionedPackages = null; - final int packageCount = mSettings.mPackages.size(); + final int packageCount = mSettings.getPackagesLocked().size(); for (int i = 0; i < packageCount; i++) { - PackageSetting ps = mSettings.mPackages.valueAt(i); + PackageSetting ps = mSettings.getPackagesLocked().valueAt(i); if (ps == null) { continue; @@ -5859,7 +5960,7 @@ public class PackageManagerService extends IPackageManager.Stub AndroidPackage pkg = mPackages.get(s.getPackageName()); if (mSettings.isEnabledAndMatchLPr(pkg, s, flags, userId)) { - PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(component.getPackageName()); if (ps == null) return null; if (shouldFilterApplicationLocked( ps, callingUid, component, TYPE_SERVICE, userId)) { @@ -5893,7 +5994,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (mSettings.isEnabledAndMatchLPr(pkg, p, flags, userId)) { - PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(component.getPackageName()); if (ps == null) return null; if (shouldFilterApplicationLocked( ps, callingUid, component, TYPE_PROVIDER, userId)) { @@ -6030,7 +6131,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = changedPackages.get(i); if (packageName != null) { // Filter out the changes if the calling package should not be able to see it. - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (shouldFilterApplicationLocked(ps, callingUid, userId)) { continue; } @@ -6051,7 +6152,7 @@ public class PackageManagerService extends IPackageManager.Stub res.addAll(mAvailableFeatures.values()); } final FeatureInfo fi = new FeatureInfo(); - fi.reqGlEsVersion = mInjector.getSystemWrapper().getPropertyInt("ro.opengles.version", + fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version", FeatureInfo.GL_ES_VERSION_UNDEFINED); res.add(fi); @@ -6777,7 +6878,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int n = 0; n < count; n++) { final ResolveInfo info = resolvedActivities.get(n); final String packageName = info.activityInfo.packageName; - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { @@ -6859,7 +6960,7 @@ public class PackageManagerService extends IPackageManager.Stub // If we have an ephemeral app, use it if (ri.activityInfo.applicationInfo.isInstantApp()) { final String packageName = ri.activityInfo.packageName; - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { @@ -7293,7 +7394,7 @@ public class PackageManagerService extends IPackageManager.Stub private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { - CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolvers(userId); + CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId); if (resolver != null) { return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); } @@ -7541,7 +7642,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = instantApps.size() - 1; i >= 0; --i) { final ResolveInfo info = instantApps.get(i); final String packageName = info.activityInfo.packageName; - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps.getInstantApp(userId)) { final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); @@ -7596,7 +7697,7 @@ public class PackageManagerService extends IPackageManager.Stub if (intent.isWebIntent() && auxiliaryResponse == null) { return result; } - final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); + final PackageSetting ps = mSettings.getPackageLPr(mInstantAppInstallerActivity.packageName); if (ps == null || !ps.readUserState(userId).isEnabled(mInstantAppInstallerActivity, 0)) { return result; @@ -7656,7 +7757,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } String packageName = riTargetUser.activityInfo.packageName; - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { continue; } @@ -7881,7 +7982,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int n=0; n<count; n++) { ResolveInfo info = candidates.get(n); String packageName = info.activityInfo.packageName; - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { // Add to the special match all list (Browser use case) if (info.handleAllWebDataURI) { @@ -7960,8 +8061,8 @@ public class PackageManagerService extends IPackageManager.Stub } else { // Browser/generic handling case. If there's a default browser, go straight // to that (but only if there is no other higher-priority match). - final String defaultBrowserPackageName = - mPermissionManager.getDefaultBrowser(userId); + final String defaultBrowserPackageName = mDefaultAppProvider.getDefaultBrowser( + userId); int maxMatchPrio = 0; ResolveInfo defaultBrowserMatch = null; final int numCandidates = matchAllList.size(); @@ -8745,8 +8846,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { ArrayList<PackageInfo> list; if (listUninstalled) { - list = new ArrayList<>(mSettings.mPackages.size()); - for (PackageSetting ps : mSettings.mPackages.values()) { + list = new ArrayList<>(mSettings.getPackagesLocked().size()); + for (PackageSetting ps : mSettings.getPackagesLocked().values()) { if (listFactory) { if (!ps.isSystem()) { continue; @@ -8858,7 +8959,7 @@ public class PackageManagerService extends IPackageManager.Stub ArrayList<PackageInfo> list = new ArrayList<>(); boolean[] tmpBools = new boolean[permissions.length]; if (listUninstalled) { - for (PackageSetting ps : mSettings.mPackages.values()) { + for (PackageSetting ps : mSettings.getPackagesLocked().values()) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } @@ -8903,8 +9004,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { ArrayList<ApplicationInfo> list; if (listUninstalled) { - list = new ArrayList<>(mSettings.mPackages.size()); - for (PackageSetting ps : mSettings.mPackages.values()) { + list = new ArrayList<>(mSettings.getPackagesLocked().size()); + for (PackageSetting ps : mSettings.getPackagesLocked().values()) { ApplicationInfo ai; int effectiveFlags = flags; if (ps.isSystem()) { @@ -8996,7 +9097,7 @@ public class PackageManagerService extends IPackageManager.Stub if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); final boolean returnAllowed = ps != null && (isCallerSameApp(packageName, callingUid) @@ -9095,7 +9196,7 @@ public class PackageManagerService extends IPackageManager.Stub if (p.isPersistent() && (!mSafeMode || p.isSystem()) && (matchesUnaware || matchesAware)) { - PackageSetting ps = mSettings.mPackages.get(p.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(p.getPackageName()); if (ps != null) { ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags, ps.readUserState(userId), userId, ps); @@ -9140,7 +9241,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { return null; } - final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); + final PackageSetting ps = mSettings.getPackageLPr(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (shouldFilterApplicationLocked(ps, callingUid, component, TYPE_PROVIDER, userId)) { @@ -9180,7 +9281,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { continue; } - final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); + final PackageSetting ps = mSettings.getPackageLPr(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (shouldFilterApplicationLocked( @@ -9209,7 +9310,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); String packageName = component.getPackageName(); - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); AndroidPackage pkg = mPackages.get(packageName); if (ps == null || pkg == null) return null; if (shouldFilterApplicationLocked( @@ -9441,8 +9542,7 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final ParsedPackage parsedPackage; - try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, mOnlyCore, mMetrics, null, - mPackageParserCallback)) { + try (PackageParser2 pp = mInjector.getScanningPackageParser()) { parsedPackage = pp.parsePackage(scanFile, parseFlags, false); } catch (PackageParserException e) { throw PackageManagerException.from(e); @@ -9736,7 +9836,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgName, getSettingsVersionForPackage(parsedPackage)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), - mSettings.mKeySetManagerService, mInjector); + mSettings.getKeySetManagerService(), mInjector); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked( reconcileResult.get(pkgName), mUserManager.getUserIds()); @@ -10047,7 +10147,7 @@ public class PackageManagerService extends IPackageManager.Stub List<PackageSetting> pkgSettings; synchronized (mLock) { pkgSettings = PackageManagerServiceUtils.getPackagesForDexopt( - mSettings.mPackages.values(), this); + mSettings.getPackagesLocked().values(), this); } List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size()); @@ -10192,7 +10292,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } - if (mInjector.getSystemWrapper().getPropertyBoolean(PRECOMPILE_LAYOUTS, false)) { + if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } @@ -10405,7 +10505,7 @@ public class PackageManagerService extends IPackageManager.Stub // Package could not be found. Report failure. return PackageDexOptimizer.DEX_OPT_FAILED; } - mPackageUsage.maybeWriteAsync(mSettings.mPackages); + mPackageUsage.maybeWriteAsync(mSettings.getPackagesLocked()); mCompilerStats.maybeWriteAsync(); } final long callingId = Binder.clearCallingIdentity(); @@ -10630,13 +10730,14 @@ public class PackageManagerService extends IPackageManager.Stub } public void shutdown() { - mPackageUsage.writeNow(mSettings.mPackages); mCompilerStats.writeNow(); mDexManager.writePackageDexUsageNow(); PackageWatchdog.getInstance(mContext).writeNow(); - // This is the last chance to write out pending restriction settings synchronized (mLock) { + mPackageUsage.writeNow(mSettings.getPackagesLocked()); + + // This is the last chance to write out pending restriction settings if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { @@ -10764,7 +10865,7 @@ public class PackageManagerService extends IPackageManager.Stub private void clearAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) { final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(pkg.getPackageName()); + ps = mSettings.getPackageLPr(pkg.getPackageName()); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; @@ -10788,7 +10889,7 @@ public class PackageManagerService extends IPackageManager.Stub private void destroyAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) { final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(pkg.getPackageName()); + ps = mSettings.getPackageLPr(pkg.getPackageName()); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; @@ -11168,7 +11269,7 @@ public class PackageManagerService extends IPackageManager.Stub } private int getVendorPartitionVersion() { - final String version = mInjector.getSystemWrapper().getProperty("ro.vndk.version"); + final String version = SystemProperties.get("ro.vndk.version"); if (!version.isEmpty()) { try { return Integer.parseInt(version); @@ -11379,7 +11480,7 @@ public class PackageManagerService extends IPackageManager.Stub // to allowlist their privileged permissions just like other // priv-apps. synchronized (mLock) { - PackageSetting platformPkgSetting = mSettings.mPackages.get("android"); + PackageSetting platformPkgSetting = mSettings.getPackageLPr("android"); if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures, pkg.getSigningDetails().signatures) != PackageManager.SIGNATURE_MATCH)) { @@ -11501,6 +11602,7 @@ public class PackageManagerService extends IPackageManager.Stub parsedPackage.setVersionCode(mSdkVersion) .setVersionCodeMajor(0); } + final AndroidPackage oldPkg = request.oldPkg; final @ParseFlags int parseFlags = request.parseFlags; final @ScanFlags int scanFlags = request.scanFlags; @@ -11540,7 +11642,7 @@ public class PackageManagerService extends IPackageManager.Stub if (reconciledPkg.installArgs != null) { InstallSource installSource = reconciledPkg.installArgs.installSource; if (installSource.initiatingPackageName != null) { - final PackageSetting ips = mSettings.mPackages.get( + final PackageSetting ips = mSettings.getPackageLPr( installSource.initiatingPackageName); if (ips != null) { installSource = installSource.setInitiatingPackageSignatures( @@ -11570,7 +11672,7 @@ public class PackageManagerService extends IPackageManager.Stub reconciledPkg.collectedSharedLibraryInfos, allUsers); } - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); if (reconciledPkg.removeAppKeySetData) { ksms.removeAppKeySetDataLPw(pkg.getPackageName()); } @@ -11932,12 +12034,13 @@ public class PackageManagerService extends IPackageManager.Stub final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride); final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp(); + final File appLib32InstallDir = getAppLib32InstallDir(); if ((scanFlags & SCAN_NEW_INSTALL) == 0) { if (needToDeriveAbi) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi"); final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi = packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp, - cpuAbiOverride); + cpuAbiOverride, appLib32InstallDir); derivedAbi.first.applyTo(parsedPackage); derivedAbi.second.applyTo(parsedPackage); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -11955,7 +12058,7 @@ public class PackageManagerService extends IPackageManager.Stub abis.applyTo(pkgSetting); final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, - isUpdatedSystemApp, sAppLib32InstallDir); + isUpdatedSystemApp, appLib32InstallDir); nativeLibraryPaths.applyTo(parsedPackage); } } else { @@ -11967,7 +12070,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, - isUpdatedSystemApp, sAppLib32InstallDir); + isUpdatedSystemApp, appLib32InstallDir); nativeLibraryPaths.applyTo(parsedPackage); if (DEBUG_ABI_SELECTION) { @@ -11993,7 +12096,7 @@ public class PackageManagerService extends IPackageManager.Stub // package path (after the rename away from the stage path). final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp, - sAppLib32InstallDir); + appLib32InstallDir); nativeLibraryPaths.applyTo(parsedPackage); } @@ -12288,7 +12391,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Make sure we're not adding any bogus keyset info - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); ksms.assertScannedPackageValid(pkg); synchronized (mLock) { @@ -12506,7 +12609,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) { // Exempt SharedUsers signed with the platform key. - PackageSetting platformPkgSetting = mSettings.mPackages.get("android"); + PackageSetting platformPkgSetting = mSettings.getPackageLPr("android"); if (!comparePackageSignatures(platformPkgSetting, pkg.getSigningDetails().signatures)) { throw new PackageManagerException("Apps that share a user with a " + @@ -12680,7 +12783,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } for (VersionedPackage dependentPackage : dependents) { - final PackageSetting ps = mSettings.mPackages.get( + final PackageSetting ps = mSettings.getPackageLPr( dependentPackage.getPackageName()); if (ps != null) { ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId); @@ -12839,7 +12942,6 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { // We don't expect installation to fail beyond this point - // Add the new setting to mSettings mSettings.insertPackageSettingLPw(pkgSetting, pkg); // Add the new setting to mPackages @@ -12849,7 +12951,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Add the package's KeySets to the global KeySetManagerService - KeySetManagerService ksms = mSettings.mKeySetManagerService; + KeySetManagerService ksms = mSettings.getKeySetManagerService(); ksms.addScannedPackageLPw(pkg); mComponentResolver.addAllComponents(pkg, chatty); @@ -13324,7 +13426,7 @@ public class PackageManagerService extends IPackageManager.Stub packageName, extras, 0, null, null, userIds, instantUserIds, mAppsFilter.getVisibilityAllowList( getPackageSettingInternal(packageName, Process.SYSTEM_UID), - userIds, mSettings.mPackages)); + userIds, mSettings.getPackagesLocked())); if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) { mHandler.post(() -> { for (int userId : userIds) { @@ -13395,7 +13497,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean sendRemoved = false; // writer synchronized (mLock) { - pkgSetting = mSettings.mPackages.get(packageName); + pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null) { return false; } @@ -13461,7 +13563,7 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { - final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null || !pkgSetting.isSystem()) { return; } @@ -13488,7 +13590,7 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { - final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); // The target app should always be in system if (pkgSetting == null || !pkgSetting.isSystem()) { return false; @@ -13575,7 +13677,7 @@ public class PackageManagerService extends IPackageManager.Stub try { // writer synchronized (mLock) { - ps = mSettings.mPackages.get(packageName); + ps = mSettings.getPackageLPr(packageName); if (ps == null) { return true; } @@ -13636,7 +13738,7 @@ public class PackageManagerService extends IPackageManager.Stub // writer synchronized (mLock) { - pkgSetting = mSettings.mPackages.get(packageName); + pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null) { return PackageManager.INSTALL_FAILED_INVALID_URI; } @@ -13671,14 +13773,16 @@ public class PackageManagerService extends IPackageManager.Stub if (installed) { if (pkgSetting.pkg != null) { + final PermissionManagerServiceInternal.PackageInstalledParams.Builder + permissionParamsBuilder = + new PermissionManagerServiceInternal.PackageInstalledParams.Builder(); if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0) { - allowlistedRestrictedPermissions = pkgSetting.pkg.getRequestedPermissions(); - } else if (allowlistedRestrictedPermissions == null) { - allowlistedRestrictedPermissions = Collections.emptyList(); + permissionParamsBuilder.setAllowlistedRestrictedPermissions( + pkgSetting.pkg.getRequestedPermissions()); } - mPermissionManager.onPackageInstalled(pkgSetting.pkg, Collections.emptyList(), - allowlistedRestrictedPermissions, MODE_DEFAULT, userId); + mPermissionManager.onPackageInstalled(pkgSetting.pkg, + permissionParamsBuilder.build(), userId); } if (pkgSetting.pkg != null) { @@ -13784,7 +13888,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = packageNames[i]; final PackageSetting pkgSetting; synchronized (mLock) { - pkgSetting = mSettings.mPackages.get(packageName); + pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { Slog.w(TAG, "Could not find package setting for package: " + packageName @@ -13882,7 +13986,7 @@ public class PackageManagerService extends IPackageManager.Stub } final PackageSetting pkgSetting; synchronized (mLock) { - pkgSetting = mSettings.mPackages.get(packageName); + pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { Slog.w(TAG, "Could not find package setting for package: " + packageName @@ -13935,9 +14039,9 @@ public class PackageManagerService extends IPackageManager.Stub private Bundle getSuspendedPackageAppExtrasInternal(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { - throw new IllegalArgumentException("Unknown target package: " + packageName); + return null; } final PackageUserState pus = ps.readUserState(userId); final Bundle allExtras = new Bundle(); @@ -13990,7 +14094,7 @@ public class PackageManagerService extends IPackageManager.Stub enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "isPackageSuspendedForUser for user " + userId); synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { throw new IllegalArgumentException("Unknown target package: " + packageName); } @@ -14008,7 +14112,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean isSuspendingAnyPackages(String suspendingPackage, int userId) { synchronized (mLock) { - for (final PackageSetting ps : mSettings.mPackages.values()) { + for (final PackageSetting ps : mSettings.getPackagesLocked().values()) { if (ps.isSuspendedBy(suspendingPackage, userId)) { return true; } @@ -14034,7 +14138,7 @@ public class PackageManagerService extends IPackageManager.Stub final IntArray unsuspendedUids = new IntArray(); synchronized (mLock) { for (String packageName : packagesToChange) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && ps.getSuspended(userId)) { ps.removeSuspension(suspendingPackagePredicate, userId); if (!ps.getSuspended(userId)) { @@ -14075,7 +14179,7 @@ public class PackageManagerService extends IPackageManager.Stub final IntArray changedUids = new IntArray(); synchronized (mLock) { for (String packageName : packagesToChange) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && ps.getDistractionFlags(userId) != 0) { ps.setDistractionFlags(0, userId); changedPackages.add(ps.name); @@ -14131,7 +14235,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageNames[i]); + final PackageSetting ps = mSettings.getPackageLPr(packageNames[i]); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]); unactionablePackages.add(packageNames[i]); @@ -14155,8 +14259,8 @@ public class PackageManagerService extends IPackageManager.Stub final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId); final long callingId = Binder.clearCallingIdentity(); try { - final String activeLauncherPackageName = mPermissionManager.getDefaultHome(userId); - final String dialerPackageName = mPermissionManager.getDefaultDialer(userId); + final String activeLauncherPackageName = mDefaultAppProvider.getDefaultHome(userId); + final String dialerPackageName = mDefaultAppProvider.getDefaultDialer(userId); for (int i = 0; i < packageNames.length; i++) { canSuspend[i] = false; final String packageName = packageNames[i]; @@ -14478,7 +14582,7 @@ public class PackageManagerService extends IPackageManager.Stub // Check if the developer wants to skip verification for ADB installs if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) { synchronized (mLock) { - if (mSettings.mPackages.get(pkgInfoLite.packageName) == null) { + if (mSettings.getPackageLPr(pkgInfoLite.packageName) == null) { // Always verify fresh install return true; } @@ -14545,7 +14649,7 @@ public class PackageManagerService extends IPackageManager.Stub return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null || shouldFilterApplicationLocked( ps, callingUid, UserHandle.getUserId(callingUid))) { @@ -14562,7 +14666,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean result = false; synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (shouldFilterApplicationLocked( ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return false; @@ -14583,7 +14687,7 @@ public class PackageManagerService extends IPackageManager.Stub return ParceledListSlice.emptyList(); } synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) { return ParceledListSlice.emptyList(); } @@ -14648,7 +14752,7 @@ public class PackageManagerService extends IPackageManager.Stub } // writer synchronized (mLock) { - PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage); + PackageSetting targetPackageSetting = mSettings.getPackageLPr(targetPackage); if (targetPackageSetting == null || shouldFilterApplicationLocked( targetPackageSetting, callingUid, UserHandle.getUserId(callingUid))) { @@ -14657,7 +14761,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageSetting installerPackageSetting; if (installerPackageName != null) { - installerPackageSetting = mSettings.mPackages.get(installerPackageName); + installerPackageSetting = mSettings.getPackageLPr(installerPackageName); if (installerPackageSetting == null) { throw new IllegalArgumentException("Unknown installer package: " + installerPackageName); @@ -14699,7 +14803,7 @@ public class PackageManagerService extends IPackageManager.Stub String targetInstallerPackageName = targetPackageSetting.installSource.installerPackageName; PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null : - mSettings.mPackages.get(targetInstallerPackageName); + mSettings.getPackageLPr(targetInstallerPackageName); if (targetInstallerPkgSetting != null) { if (compareSignatures(callerSignature, @@ -14750,7 +14854,7 @@ public class PackageManagerService extends IPackageManager.Stub mInjector.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(), callerPackageName); synchronized (mLock) { - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { throw new IllegalArgumentException("Unknown target package " + packageName); } @@ -15208,6 +15312,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean forceQueryableOverride; final int mDataLoaderType; final long requiredInstalledVersionCode; + final PackageLite mPackageLite; InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, int installFlags, InstallSource installSource, String volumeUuid, @@ -15229,11 +15334,13 @@ public class PackageManagerService extends IPackageManager.Stub this.forceQueryableOverride = false; this.mDataLoaderType = DataLoaderType.NONE; this.requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST; + this.mPackageLite = null; } InstallParams(File stagedDir, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, InstallSource installSource, - UserHandle user, SigningDetails signingDetails, int installerUid) { + UserHandle user, SigningDetails signingDetails, int installerUid, + PackageLite packageLite) { super(user); origin = OriginInfo.fromStagedFile(stagedDir); move = null; @@ -15252,6 +15359,7 @@ public class PackageManagerService extends IPackageManager.Stub mDataLoaderType = (sessionParams.dataLoaderParams != null) ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE; requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode; + mPackageLite = packageLite; } @Override @@ -15336,7 +15444,8 @@ public class PackageManagerService extends IPackageManager.Stub try { mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0); pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext, - origin.resolvedPath, installFlags, packageAbiOverride); + mPackageLite, origin.resolvedPath, installFlags, + packageAbiOverride); } catch (InstallerException e) { Slog.w(TAG, "Failed to free cache", e); } @@ -15397,7 +15506,7 @@ public class PackageManagerService extends IPackageManager.Stub */ public void handleStartCopy() { PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext, - origin.resolvedPath, installFlags, packageAbiOverride); + mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride); // For staged session, there is a delay between its verification and install. Device // state can change within this delay and hence we need to re-verify certain conditions. @@ -15517,9 +15626,11 @@ public class PackageManagerService extends IPackageManager.Stub private boolean mWaitForEnableRollbackToComplete; private int mRet; + final PackageLite mPackageLite; + VerificationParams(UserHandle user, File stagedDir, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, InstallSource installSource, - int installerUid, SigningDetails signingDetails, int sessionId) { + int installerUid, SigningDetails signingDetails, int sessionId, PackageLite lite) { super(user); origin = OriginInfo.fromStagedFile(stagedDir); this.observer = observer; @@ -15537,6 +15648,7 @@ public class PackageManagerService extends IPackageManager.Stub mDataLoaderType = (sessionParams.dataLoaderParams != null) ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE; mSessionId = sessionId; + mPackageLite = lite; } @Override @@ -15554,7 +15666,7 @@ public class PackageManagerService extends IPackageManager.Stub } PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext, - origin.resolvedPath, installFlags, packageAbiOverride); + mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride); mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags); if (mRet != INSTALL_SUCCEEDED) { @@ -16480,12 +16592,10 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath()); synchronized (mLock) { -// NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions - mPermissionManager.updatePermissions(pkgName, pkg); // For system-bundled packages, we assume that installing an upgraded version // of the package implies that the user actually wants to run that new code, // so we enable the package. - final PackageSetting ps = mSettings.mPackages.get(pkgName); + final PackageSetting ps = mSettings.getPackageLPr(pkgName); final int userId = installArgs.user.getIdentifier(); if (ps != null) { if (pkg.isSystem()) { @@ -16530,7 +16640,7 @@ public class PackageManagerService extends IPackageManager.Stub // TODO(146804378): Support overlaying static shared libraries continue; } - final PackageSetting libPs = mSettings.mPackages.get( + final PackageSetting libPs = mSettings.getPackageLPr( sharedLib.getPackageName()); if (libPs == null) { continue; @@ -16943,8 +17053,7 @@ public class PackageManagerService extends IPackageManager.Stub && compareSignatures(sharedUserSignatures, parsedPackage.getSigningDetails().signatures) != PackageManager.SIGNATURE_MATCH) { - if (injector.getSystemWrapper() - .getPropertyInt("ro.product.first_api_level", 0) <= 29) { + if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) { // Mismatched signatures is an error and silently skipping system // packages will likely break the device in unforeseen ways. // However, we allow the device to boot anyway because, prior to Q, @@ -17134,7 +17243,7 @@ public class PackageManagerService extends IPackageManager.Stub reconciledPkg.pkgSetting.lastUpdateTime = System.currentTimeMillis(); res.removedInfo.broadcastAllowList = mAppsFilter.getVisibilityAllowList( - reconciledPkg.pkgSetting, request.mAllUsers, mSettings.mPackages); + reconciledPkg.pkgSetting, request.mAllUsers, mSettings.getPackagesLocked()); if (reconciledPkg.prepareResult.system) { // Remove existing system package removePackageLI(oldPackage, true); @@ -17158,7 +17267,7 @@ public class PackageManagerService extends IPackageManager.Stub executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName, true, request.mAllUsers, false, parsedPackage); } catch (SystemDeleteException e) { - if (Build.IS_ENG) { + if (mIsEngBuild) { throw new RuntimeException("Unexpected failure", e); // ignore; not possible for non-system app } @@ -17179,7 +17288,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Update the in-memory copy of the previous code paths. - PackageSetting ps1 = mSettings.mPackages.get( + PackageSetting ps1 = mSettings.getPackageLPr( reconciledPkg.prepareResult.existingPackage.getPackageName()); if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP) == 0) { @@ -17208,7 +17317,7 @@ public class PackageManagerService extends IPackageManager.Stub AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, request.mAllUsers); updateSettingsLI(pkg, reconciledPkg.installArgs, request.mAllUsers, res); - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { res.newUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true); ps.setUpdateAvailable(false /*updateAvailable*/); @@ -17318,7 +17427,7 @@ public class PackageManagerService extends IPackageManager.Stub try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages"); reconciledPackages = reconcilePackagesLocked( - reconcileRequest, mSettings.mKeySetManagerService, mInjector); + reconcileRequest, mSettings.getKeySetManagerService(), mInjector); } catch (ReconcileFailure e) { for (InstallRequest request : requests) { request.installResult.setError("Reconciliation failed...", e); @@ -17453,7 +17562,7 @@ public class PackageManagerService extends IPackageManager.Stub if (performDexopt) { // Compile the layout resources. - if (mInjector.getSystemWrapper().getPropertyBoolean(PRECOMPILE_LAYOUTS, false)) { + if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts"); mViewCompiler.compileLayouts(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -17796,7 +17905,7 @@ public class PackageManagerService extends IPackageManager.Stub final SigningDetails sourceSigningDetails = (sourcePackageSetting == null ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails()); - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); if (sourcePackageName.equals(parsedPackage.getPackageName()) && (ksms.shouldCheckUpgradeKeySetLocked( sourcePackageSetting, scanFlags))) { @@ -17880,8 +17989,7 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final ParsedPackage parsedPackage; - try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, false, mMetrics, null, - mPackageParserCallback)) { + try (PackageParser2 pp = mInjector.getPreparingPackageParser()) { parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false); AndroidPackageUtils.validatePackageDexMetadata(parsedPackage); } catch (PackageParserException e) { @@ -17930,8 +18038,8 @@ public class PackageManagerService extends IPackageManager.Stub if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) { parsedPackage.setSigningDetails(args.signingDetails); } else { - parsedPackage.setSigningDetails( - ParsingPackageUtils.getSigningDetails(parsedPackage, false /* skipVerify */)); + parsedPackage.setSigningDetails(ParsingPackageUtils.getSigningDetails( + parsedPackage, false /* skipVerify */)); } } catch (PackageParserException e) { throw new PrepareFailure("Failed collect during installPackageLI", e); @@ -17995,7 +18103,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - PackageSetting ps = mSettings.mPackages.get(pkgName); + PackageSetting ps = mSettings.getPackageLPr(pkgName); if (ps != null) { if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); @@ -18014,7 +18122,7 @@ public class PackageManagerService extends IPackageManager.Stub // Quick validity check that we're signed correctly if updating; // we'll check this again later when scanning, but we want to // bail early here before tripping over redefined permissions. - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) { if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) { throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " @@ -18195,7 +18303,7 @@ public class PackageManagerService extends IPackageManager.Stub scanFlags |= SCAN_MOVE; synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(pkgName); + final PackageSetting ps = mSettings.getPackageLPr(pkgName); if (ps == null) { res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Missing settings for moved package " + pkgName); @@ -18224,7 +18332,7 @@ public class PackageManagerService extends IPackageManager.Stub final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage, isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred, - abiOverride); + abiOverride, mAppLib32InstallDir); derivedAbi.first.applyTo(parsedPackage); derivedAbi.second.applyTo(parsedPackage); } catch (PackageManagerException pme) { @@ -18293,11 +18401,11 @@ public class PackageManagerService extends IPackageManager.Stub "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage); } - ps = mSettings.mPackages.get(pkgName11); + ps = mSettings.getPackageLPr(pkgName11); disabledPs = mSettings.getDisabledSystemPkgLPr(ps); // verify signatures are valid - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) { if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) { throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, @@ -18500,7 +18608,7 @@ public class PackageManagerService extends IPackageManager.Stub ArrayMap<String, String> fsverityCandidates = new ArrayMap<>(); if (legacyMode) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); + final PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName()); if (ps != null && ps.isPrivileged()) { fsverityCandidates.put(pkg.getBaseApkPath(), null); if (pkg.getSplitCodePaths() != null) { @@ -18918,7 +19026,7 @@ public class PackageManagerService extends IPackageManager.Stub // Queue up an async operation since the package deletion may take a little while. mHandler.post(() -> { int returnCode; - final PackageSetting ps = mSettings.mPackages.get(internalPackageName); + final PackageSetting ps = mSettings.getPackageLPr(internalPackageName); boolean doDeletePackage = true; if (ps != null) { final boolean targetIsInstantApp = @@ -19196,7 +19304,7 @@ public class PackageManagerService extends IPackageManager.Stub int[] allUsers; /** enabled state of the uninstalled application */ synchronized (mLock) { - uninstalledPs = mSettings.mPackages.get(packageName); + uninstalledPs = mSettings.getPackageLPr(packageName); if (uninstalledPs == null) { Slog.w(TAG, "Not removing non-existent package " + packageName); return PackageManager.DELETE_FAILED_INTERNAL_ERROR; @@ -19495,19 +19603,26 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true); clearDefaultBrowserIfNeeded(packageName); - mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName); + mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName); mAppsFilter.removePackage(getPackageSetting(packageName)); removedAppId = mSettings.removePackageLPw(packageName); if (outInfo != null) { outInfo.removedAppId = removedAppId; } - final SharedUserSetting sus = deletedPs.getSharedUser(); - List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : null; - if (sharedUserPkgs == null) { - sharedUserPkgs = Collections.emptyList(); + if (!mSettings.isDisabledSystemPackageLPr(packageName)) { + // If we don't have a disabled system package to reinstall, the package is + // really gone and its permission state should be removed. + final SharedUserSetting sus = deletedPs.getSharedUser(); + List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() + : null; + if (sharedUserPkgs == null) { + sharedUserPkgs = Collections.emptyList(); + } + for (final int userId : allUserHandles) { + mPermissionManager.onPackageUninstalled(packageName, deletedPs.appId, + deletedPs.pkg, sharedUserPkgs, userId); + } } - mPermissionManager.onPackageStateRemoved(packageName, deletedPs.appId, - deletedPs.pkg, sharedUserPkgs); clearPackagePreferredActivitiesLPw( deletedPs.name, changedUsers, UserHandle.USER_ALL); } @@ -19638,7 +19753,7 @@ public class PackageManagerService extends IPackageManager.Stub // We've re-installed the stub; make sure it's disabled here. If package was // originally enabled, we'll install the compressed version of the application // and re-enable it afterward. - final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName()); + final PackageSetting stubPs = mSettings.getPackageLPr(deletedPkg.getPackageName()); if (stubPs != null) { int userId = action.user == null ? UserHandle.USER_ALL : action.user.getIdentifier(); @@ -19694,11 +19809,7 @@ public class PackageManagerService extends IPackageManager.Stub // writer synchronized (mLock) { - PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); - - // The update permissions method below will take care of removing obsolete permissions - // and granting install permissions. - mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); + PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName()); final boolean applyUserRestrictions = origUserHandles != null; if (applyUserRestrictions) { @@ -19718,8 +19829,6 @@ public class PackageManagerService extends IPackageManager.Stub if (installed) { ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); } - - mSettings.writeRuntimePermissionsForUserLPr(userId, false); } // Regardless of writeSettings we need to ensure that this restriction // state propagation is persisted @@ -19728,6 +19837,17 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.writeKernelMappingLPr(ps); } } + + for (final int userId : allUserHandles) { + // The method below will take care of removing obsolete permissions and granting + // install permissions. + mPermissionManager.onPackageInstalled(pkg, + PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, userId); + if (applyUserRestrictions) { + mSettings.writeRuntimePermissionsForUserLPr(userId, false); + } + } + // can downgrade to reader here if (writeSettings) { writeSettingsLPrTEMP(); @@ -19782,7 +19902,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean getBlockUninstallForUser(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null || shouldFilterApplicationLocked(ps, Binder.getCallingUid(), userId)) { return false; } @@ -19794,7 +19914,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) { enforceSystemOrRoot("setRequiredForSystemUser can only be run by the system or root"); synchronized (mLock) { - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { Log.w(TAG, "Package doesn't exist: " + packageName); return false; @@ -19862,7 +19982,7 @@ public class PackageManagerService extends IPackageManager.Stub ParsedPackage replacingPackage) { final DeletePackageAction action; synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps); action = mayDeletePackageLocked(outInfo, ps, disabledPs, flags, user); } @@ -20026,6 +20146,11 @@ public class PackageManagerService extends IPackageManager.Stub destroyAppProfilesLIF(pkg); + final SharedUserSetting sus = ps.getSharedUser(); + List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : null; + if (sharedUserPkgs == null) { + sharedUserPkgs = Collections.emptyList(); + } final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] {userId}; for (int nextUserId : userIds) { @@ -20040,7 +20165,8 @@ public class PackageManagerService extends IPackageManager.Stub clearDefaultBrowserIfNeededForUser(ps.name, nextUserId); removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), nextUserId, ps.appId); clearPackagePreferredActivities(ps.name, nextUserId); - mPermissionManager.resetRuntimePermissions(pkg, nextUserId); + mPermissionManager.onPackageUninstalled(ps.name, ps.appId, pkg, sharedUserPkgs, + nextUserId); } if (outInfo != null) { @@ -20146,7 +20272,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageSetting ps; synchronized (mLock) { pkg = mPackages.get(packageName); - ps = mSettings.mPackages.get(packageName); + ps = mSettings.getPackageLPr(packageName); if (pkg == null) { if (ps != null) { pkg = ps.pkg; @@ -20283,7 +20409,7 @@ public class PackageManagerService extends IPackageManager.Stub private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) { final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(packageName); + ps = mSettings.getPackageLPr(packageName); if (ps == null) { Slog.w(TAG, "Failed to find settings for " + packageName); return false; @@ -20590,10 +20716,10 @@ public class PackageManagerService extends IPackageManager.Stub } private void clearDefaultBrowserIfNeededForUser(String packageName, int userId) { - final String defaultBrowserPackageName = mPermissionManager.getDefaultBrowser(userId); + final String defaultBrowserPackageName = mDefaultAppProvider.getDefaultBrowser(userId); if (!TextUtils.isEmpty(defaultBrowserPackageName)) { if (packageName.equals(defaultBrowserPackageName)) { - mPermissionManager.setDefaultBrowser(null, true, true, userId); + mDefaultAppProvider.setDefaultBrowser(null, true, userId); } } } @@ -20608,14 +20734,13 @@ public class PackageManagerService extends IPackageManager.Stub // If this browser is restored from user's backup, do not clear // default-browser state for this user if (installReason != PackageManager.INSTALL_REASON_DEVICE_RESTORE) { - mPermissionManager.setDefaultBrowser(null, true, true, userId); + mDefaultAppProvider.setDefaultBrowser(null, true, userId); } } // We may also need to apply pending (restored) runtime permission grants // within these users. - mPermissionManager.restoreDelayedRuntimePermissions(packageName, - UserHandle.of(userId)); + mPermissionManager.restoreDelayedRuntimePermissions(packageName, userId); // Persistent preferred activity might have came into effect due to this // install. @@ -20647,7 +20772,7 @@ public class PackageManagerService extends IPackageManager.Stub // significant refactoring to keep all default apps in the package // manager (cleaner but more work) or have the services provide // callbacks to the package manager to request a default app reset. - mPermissionManager.setDefaultBrowser(null, true, true, userId); + mDefaultAppProvider.setDefaultBrowser(null, true, userId); resetNetworkPolicies(userId); synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); @@ -20881,8 +21006,7 @@ public class PackageManagerService extends IPackageManager.Stub defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1); } if (defaultBrowser != null) { - mPermissionManager - .setDefaultBrowser(defaultBrowser, false, false, userId1); + mDefaultAppProvider.setDefaultBrowser(defaultBrowser, false, userId1); } }); } catch (Exception e) { @@ -21122,7 +21246,7 @@ public class PackageManagerService extends IPackageManager.Stub } allHomeCandidates.addAll(resolveInfos); - final String packageName = mPermissionManager.getDefaultHome(userId); + final String packageName = mDefaultAppProvider.getDefaultHome(userId); if (packageName == null) { return null; } @@ -21176,7 +21300,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = preferredResolveInfo != null && preferredResolveInfo.activityInfo != null ? preferredResolveInfo.activityInfo.packageName : null; - final String currentPackageName = mPermissionManager.getDefaultHome(userId); + final String currentPackageName = mDefaultAppProvider.getDefaultHome(userId); if (TextUtils.equals(currentPackageName, packageName)) { return false; } @@ -21191,12 +21315,12 @@ public class PackageManagerService extends IPackageManager.Stub // Keep the default home package in RoleManager. return false; } - mPermissionManager.setDefaultHome(packageName, userId, (successful) -> { - if (successful) { - postPreferredActivityChangedBroadcast(userId); - } - }); - return true; + return mDefaultAppProvider.setDefaultHome(packageName, userId, mContext.getMainExecutor(), + successful -> { + if (successful) { + postPreferredActivityChangedBroadcast(userId); + } + }); } @Override @@ -21459,7 +21583,7 @@ public class PackageManagerService extends IPackageManager.Stub public void setUpdateAvailable(String packageName, boolean updateAvailable) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); synchronized (mLock) { - final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting != null) { pkgSetting.setUpdateAvailable(updateAvailable); } @@ -21584,7 +21708,7 @@ public class PackageManagerService extends IPackageManager.Stub // reader synchronized (mLock) { - pkgSetting = mSettings.mPackages.get(packageName); + pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null) { if (!isCallerInstantApp) { if (className == null) { @@ -21848,7 +21972,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } broadcastAllowList = isInstantApp ? null : mAppsFilter.getVisibilityAllowList(setting, - userIds, mSettings.mPackages); + userIds, mSettings.getPackagesLocked()); } sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null, userIds, instantUserIds, broadcastAllowList); @@ -21868,7 +21992,7 @@ public class PackageManagerService extends IPackageManager.Stub true /* checkShell */, "stop package"); // writer synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (!shouldFilterApplicationLocked(ps, callingUid, userId) && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, allowedByPermission, callingUid, userId)) { @@ -21887,7 +22011,7 @@ public class PackageManagerService extends IPackageManager.Stub } String installerPackageName = installSource.installerPackageName; if (installerPackageName != null) { - final PackageSetting ps = mSettings.mPackages.get(installerPackageName); + final PackageSetting ps = mSettings.getPackageLPr(installerPackageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) { installerPackageName = null; @@ -21916,7 +22040,7 @@ public class PackageManagerService extends IPackageManager.Stub installerPackageName = installSource.installerPackageName; if (installerPackageName != null) { - final PackageSetting ps = mSettings.mPackages.get(installerPackageName); + final PackageSetting ps = mSettings.getPackageLPr(installerPackageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { installerPackageName = null; } @@ -21941,7 +22065,7 @@ public class PackageManagerService extends IPackageManager.Stub initiatingPackageName = installerPackageName; } else { initiatingPackageName = installSource.initiatingPackageName; - final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName); + final PackageSetting ps = mSettings.getPackageLPr(initiatingPackageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { initiatingPackageName = null; } @@ -21950,7 +22074,7 @@ public class PackageManagerService extends IPackageManager.Stub originatingPackageName = installSource.originatingPackageName; if (originatingPackageName != null) { - final PackageSetting ps = mSettings.mPackages.get(originatingPackageName); + final PackageSetting ps = mSettings.getPackageLPr(originatingPackageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { originatingPackageName = null; } @@ -21983,7 +22107,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") @Nullable private InstallSource getInstallSourceLocked(String packageName, int callingUid) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); // Installer info for Apex is not stored in PackageManager if (ps == null && mApexManager.isApexPackage(packageName)) { @@ -22142,6 +22266,24 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.systemReady(); + int[] grantPermissionsUserIds = EMPTY_INT_ARRAY; + for (int userId : UserManagerService.getInstance().getUserIds()) { + if (mPmInternal.isPermissionUpgradeNeeded(userId)) { + grantPermissionsUserIds = ArrayUtils.appendInt( + grantPermissionsUserIds, userId); + } + } + // If we upgraded grant all default permissions before kicking off. + for (int userId : grantPermissionsUserIds) { + mLegacyPermissionManager.grantDefaultPermissions(userId); + } + if (grantPermissionsUserIds == EMPTY_INT_ARRAY) { + // If we did not grant default permissions, we preload from this the + // default permission exceptions lazily to ensure we don't hit the + // disk on a new user creation. + mLegacyPermissionManager.scheduleReadDefaultPermissionExceptions(); + } + if (mInstantAppResolverConnection != null) { mContext.registerReceiver(new BroadcastReceiver() { @Override @@ -22646,13 +22788,14 @@ public class PackageManagerService extends IPackageManager.Stub && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED) && packageName == null) { pw.println(); - int count = mSettings.mPackages.size(); + int count = mSettings.getPackagesLocked().size(); if (count == 0) { pw.println("No applications!"); pw.println(); } else { final String prefix = " "; - Collection<PackageSetting> allPackageSettings = mSettings.mPackages.values(); + Collection<PackageSetting> allPackageSettings = + mSettings.getPackagesLocked().values(); if (allPackageSettings.size() == 0) { pw.println("No domain preferred apps!"); pw.println(); @@ -22709,7 +22852,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) { - mSettings.mKeySetManagerService.dumpLPr(pw, packageName, dumpState); + mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState); } if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) { @@ -22840,7 +22983,7 @@ public class PackageManagerService extends IPackageManager.Stub if (ArrayUtils.isEmpty(apkList)) { return; } - String sku = mInjector.getSystemWrapper().getProperty("ro.boot.hardware.sku"); + String sku = SystemProperties.get("ro.boot.hardware.sku"); if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) { return; } @@ -22936,7 +23079,7 @@ public class PackageManagerService extends IPackageManager.Stub ipw.increaseIndent(); Collection<PackageSetting> pkgSettings; if (packageName != null) { - PackageSetting targetPkgSetting = mSettings.mPackages.get(packageName); + PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName); if (targetPkgSetting != null) { pkgSettings = Collections.singletonList(targetPkgSetting); } else { @@ -22944,7 +23087,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } } else { - pkgSettings = mSettings.mPackages.values(); + pkgSettings = mSettings.getPackagesLocked().values(); } for (PackageSetting pkgSetting : pkgSettings) { @@ -23245,7 +23388,7 @@ public class PackageManagerService extends IPackageManager.Stub // Normalize package name to handle renamed packages packageName = normalizePackageNameLPr(packageName); - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { throw new PackageManagerException("Package " + packageName + " is unknown"); } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) { @@ -23262,9 +23405,9 @@ public class PackageManagerService extends IPackageManager.Stub private List<String> collectAbsoluteCodePaths() { synchronized (mLock) { List<String> codePaths = new ArrayList<>(); - final int packageCount = mSettings.mPackages.size(); + final int packageCount = mSettings.getPackagesLocked().size(); for (int i = 0; i < packageCount; i++) { - final PackageSetting ps = mSettings.mPackages.valueAt(i); + final PackageSetting ps = mSettings.getPackagesLocked().valueAt(i); codePaths.add(ps.getPath().getAbsolutePath()); } return codePaths; @@ -23466,7 +23609,7 @@ public class PackageManagerService extends IPackageManager.Stub private void prepareAppDataAfterInstallLIF(AndroidPackage pkg) { final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(pkg.getPackageName()); + ps = mSettings.getPackageLPr(pkg.getPackageName()); mSettings.writeKernelMappingLPr(ps); } @@ -23543,7 +23686,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(pkg.getPackageName()); + ps = mSettings.getPackageLPr(pkg.getPackageName()); } final String volumeUuid = pkg.getVolumeUuid(); final String packageName = pkg.getPackageName(); @@ -23562,7 +23705,7 @@ public class PackageManagerService extends IPackageManager.Stub // Note: this code block is executed with the Installer lock // already held, since it's invoked as a side-effect of // executeBatchLI() - if ((e != null) && pkg.isSystem()) { + if (e != null) { logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName + ", but trying to recover: " + e); destroyAppDataLeafLIF(pkg, userId, flags); @@ -23738,7 +23881,7 @@ public class PackageManagerService extends IPackageManager.Stub mPackageName = packageName; mWeFroze = mFrozenPackages.add(mPackageName); - final PackageSetting ps = mSettings.mPackages.get(mPackageName); + final PackageSetting ps = mSettings.getPackageLPr(mPackageName); if (ps != null) { killApplication(ps.name, ps.appId, userId, killReason); } @@ -23820,7 +23963,7 @@ public class PackageManagerService extends IPackageManager.Stub // reader synchronized (mLock) { final AndroidPackage pkg = mPackages.get(packageName); - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (pkg == null || ps == null || shouldFilterApplicationLocked(ps, callingUid, user.getIdentifier())) { @@ -24151,9 +24294,9 @@ public class PackageManagerService extends IPackageManager.Stub private void removeUnusedPackagesLPw(UserManagerService userManager, final int userId) { final boolean DEBUG_CLEAN_APKS = false; int [] users = userManager.getUserIds(); - final int numPackages = mSettings.mPackages.size(); + final int numPackages = mSettings.getPackagesLocked().size(); for (int index = 0; index < numPackages; index++) { - final PackageSetting ps = mSettings.mPackages.valueAt(index); + final PackageSetting ps = mSettings.getPackagesLocked().valueAt(index); if (ps.pkg == null) { continue; } @@ -24222,14 +24365,9 @@ public class PackageManagerService extends IPackageManager.Stub Slog.d(TAG, "onNewUserCreated(id=" + userId + ", convertedFromPreCreated=" + convertedFromPreCreated + ")"); } - if (!convertedFromPreCreated) { - mPermissionManager.onNewUserCreated(userId); - return; - } - if (!readPermissionStateForUser(userId)) { - // Could not read the existing permissions, re-grant them. - Slog.i(TAG, "re-granting permissions for pre-created user " + userId); - mPermissionManager.onNewUserCreated(userId); + if (!convertedFromPreCreated || !readPermissionStateForUser(userId)) { + mPermissionManager.onUserCreated(userId); + mLegacyPermissionManager.grantDefaultPermissions(userId); } } @@ -24322,7 +24460,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.w(TAG, "KeySet requested for filtered package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias)); } } @@ -24351,7 +24489,7 @@ public class PackageManagerService extends IPackageManager.Stub && Process.SYSTEM_UID != callingUid) { throw new SecurityException("May not access signing KeySet of other apps."); } - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); return new KeySet(ksms.getSigningKeySetByPackageNameLPr(packageName)); } } @@ -24375,7 +24513,7 @@ public class PackageManagerService extends IPackageManager.Stub } IBinder ksh = ks.getToken(); if (ksh instanceof KeySetHandle) { - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ksh); } return false; @@ -24401,7 +24539,7 @@ public class PackageManagerService extends IPackageManager.Stub } IBinder ksh = ks.getToken(); if (ksh instanceof KeySetHandle) { - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ksh); } return false; @@ -24410,7 +24548,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") private void deletePackageIfUnusedLPr(final String packageName) { - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { return; } @@ -24746,7 +24884,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPlatformSigned(String packageName) { - PackageSetting packageSetting = mSettings.mPackages.get(packageName); + PackageSetting packageSetting = mSettings.getPackageLPr(packageName); if (packageSetting == null) { return false; } @@ -24946,7 +25084,7 @@ public class PackageManagerService extends IPackageManager.Stub private String[] getKnownPackageNamesInternal(int knownPackage, int userId) { switch (knownPackage) { case PackageManagerInternal.PACKAGE_BROWSER: - return new String[]{mPermissionManager.getDefaultBrowser(userId)}; + return new String[] { mDefaultAppProvider.getDefaultBrowser(userId) }; case PackageManagerInternal.PACKAGE_INSTALLER: return filterOnlySystemPackages(mRequiredInstallerPackage); case PackageManagerInternal.PACKAGE_SETUP_WIZARD: @@ -25035,7 +25173,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public long getCeDataInode(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { return ps.getCeDataInode(userId); } @@ -25046,7 +25184,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); final Bundle allExtras = new Bundle(); if (ps != null) { final PackageUserState pus = ps.readUserState(userId); @@ -25068,7 +25206,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPackageSuspended(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); return (ps != null) ? ps.getSuspended(userId) : false; } } @@ -25113,7 +25251,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getSuspendingPackage(String suspendedPackage, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(suspendedPackage); + final PackageSetting ps = mSettings.getPackageLPr(suspendedPackage); if (ps != null) { final PackageUserState pus = ps.readUserState(userId); if (pus.suspended) { @@ -25135,7 +25273,7 @@ public class PackageManagerService extends IPackageManager.Stub public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, String suspendingPackage, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(suspendedPackage); + final PackageSetting ps = mSettings.getPackageLPr(suspendedPackage); if (ps != null) { final PackageUserState pus = ps.readUserState(userId); if (pus.suspended) { @@ -25151,7 +25289,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int getDistractingPackageRestrictions(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE; } } @@ -25247,7 +25385,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPackageEphemeral(int userId, String packageName) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); return ps != null ? ps.getInstantApp(userId) : false; } } @@ -25438,7 +25576,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } for (VersionedPackage dependent : dependents) { - final PackageSetting ps = mSettings.mPackages.get( + final PackageSetting ps = mSettings.getPackageLPr( dependent.getPackageName()); if (ps == null) { continue; @@ -25449,7 +25587,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - final PackageSetting ps = mSettings.mPackages.get(targetPackageName); + final PackageSetting ps = mSettings.getPackageLPr(targetPackageName); ps.setOverlayPaths(overlayPaths, userId); outUpdatedPackageNames.add(targetPackageName); @@ -25519,7 +25657,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean canAccessComponent(int callingUid, ComponentName component, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); + final PackageSetting ps = mSettings.getPackageLPr(component.getPackageName()); return ps != null && !PackageManagerService.this.shouldFilterApplicationLocked( ps, callingUid, component, TYPE_UNKNOWN, userId); } @@ -25585,8 +25723,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void forEachPackageSetting(Consumer<PackageSetting> actionLocked) { synchronized (mLock) { - for (int index = 0; index < mSettings.mPackages.size(); index++) { - actionLocked.accept(mSettings.mPackages.valueAt(index)); + for (int index = 0; index < mSettings.getPackagesLocked().size(); index++) { + actionLocked.accept(mSettings.getPackagesLocked().valueAt(index)); } } } @@ -25762,7 +25900,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } final PackageSetting installerPackageSetting = - mSettings.mPackages.get(packageSetting.installSource.installerPackageName); + mSettings.getPackageLPr(packageSetting.installSource.installerPackageName); return installerPackageSetting != null && UserHandle.isSameApp(installerPackageSetting.appId, callingUid); } @@ -25776,18 +25914,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public void setReadExternalStorageEnforced(boolean enforced) { - synchronized (mLock) { - if (mSettings.mReadExternalStorageEnforced != null - && mSettings.mReadExternalStorageEnforced == enforced) { - return; - } - mSettings.mReadExternalStorageEnforced = enforced ? Boolean.TRUE : Boolean.FALSE; - writeSettingsLPrTEMP(); - } - } - - @Override public void setIntegrityVerificationResult(int verificationId, int verificationResult) { final Message msg = mHandler.obtainMessage(INTEGRITY_VERIFICATION_COMPLETE); msg.arg1 = verificationId; @@ -25863,7 +25989,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageManagerInternal.InstalledLoadingProgressCallback callback) { final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(packageName); + ps = mSettings.getPackageLPr(packageName); if (ps == null) { Slog.w(TAG, "Failed unregistering loading progress callback. Package " + packageName + " is not installed"); @@ -25926,7 +26052,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") @NonNull private String[] getSharedUserPackagesForPackageLocked(String packageName, int userId) { - final PackageSetting packageSetting = mSettings.mPackages.get(packageName); + final PackageSetting packageSetting = mSettings.getPackageLPr(packageName); if (packageSetting == null || !packageSetting.isSharedUser()) { return EmptyArray.STRING; } @@ -26003,7 +26129,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { packageName = resolveInternalPackageNameInternalLocked( packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid); - return mSettings.mPackages.get(packageName); + return mSettings.getPackageLPr(packageName); } } @@ -26032,7 +26158,9 @@ public class PackageManagerService extends IPackageManager.Stub } boolean isHistoricalPackageUsageAvailable() { - return mPackageUsage.isHistoricalPackageUsageAvailable(); + synchronized (mLock) { + return mPackageUsage.isHistoricalPackageUsageAvailable(); + } } /** @@ -26092,7 +26220,7 @@ public class PackageManagerService extends IPackageManager.Stub enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "get install reason"); synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (shouldFilterApplicationLocked(ps, callingUid, userId)) { return PackageManager.INSTALL_REASON_UNKNOWN; } @@ -26265,7 +26393,7 @@ public class PackageManagerService extends IPackageManager.Stub long currentTimeInMillis = System.currentTimeMillis(); synchronized (mLock) { for (AndroidPackage pkg : mPackages.values()) { - PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName()); if (ps == null) { continue; } @@ -26377,7 +26505,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) { - boolean changed = mSettings.mPackages.get(packageName) + boolean changed = mSettings.getPackageLPr(packageName) .setMimeGroup(mimeGroup, mimeTypes); if (changed) { @@ -26387,7 +26515,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<String> getMimeGroup(String packageName, String mimeGroup) { - return mSettings.mPackages.get(packageName).getMimeGroup(mimeGroup); + return mSettings.getPackageLPr(packageName).getMimeGroup(mimeGroup); } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 9f1d6566e279..c77eda161c67 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -819,8 +819,8 @@ public class PackageManagerServiceUtils { /** * Parse given package and return minimal details. */ - public static PackageInfoLite getMinimalPackageInfo(Context context, String packagePath, - int flags, String abiOverride) { + public static PackageInfoLite getMinimalPackageInfo(Context context, + PackageParser.PackageLite pkg, String packagePath, int flags, String abiOverride) { final PackageInfoLite ret = new PackageInfoLite(); if (packagePath == null) { Slog.i(TAG, "Invalid package file " + packagePath); @@ -829,14 +829,10 @@ public class PackageManagerServiceUtils { } final File packageFile = new File(packagePath); - final PackageParser.PackageLite pkg; final long sizeBytes; try { - pkg = PackageParser.parsePackageLite(packageFile, 0); sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride); - } catch (PackageParserException | IOException e) { - Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e); - + } catch (IOException e) { if (!packageFile.exists()) { ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI; } else { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 9aa1a621a760..318b2293a13a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -146,7 +146,7 @@ class PackageManagerShellCommand extends ShellCommand { final IPackageManager mInterface; final IPermissionManager mPermissionManager; - final Context mShellPackageContext; + final Context mContext; final private WeakHashMap<String, Resources> mResourceCache = new WeakHashMap<String, Resources>(); int mTargetUser; @@ -158,12 +158,7 @@ class PackageManagerShellCommand extends ShellCommand { PackageManagerService service, IPermissionManager permissionManager, Context context) { mInterface = service; mPermissionManager = permissionManager; - try { - mShellPackageContext = context.createPackageContext("com.android.shell", 0); - } catch (NameNotFoundException e) { - // should not happen - throw new RuntimeException(e); - } + mContext = context; } @Override @@ -486,8 +481,17 @@ class PackageManagerShellCommand extends ShellCommand { return 1; } + final Context shellPackageContext; + try { + shellPackageContext = mContext.createPackageContextAsUser( + "com.android.shell", 0, Binder.getCallingUserHandle()); + } catch (NameNotFoundException e) { + // should not happen + throw new RuntimeException(e); + } + final LocalIntentReceiver receiver = new LocalIntentReceiver(); - RollbackManager rm = mShellPackageContext.getSystemService(RollbackManager.class); + RollbackManager rm = shellPackageContext.getSystemService(RollbackManager.class); RollbackInfo rollback = null; for (RollbackInfo r : rm.getAvailableRollbacks()) { for (PackageRollbackInfo info : r.getPackages()) { @@ -549,7 +553,8 @@ class PackageManagerShellCommand extends ShellCommand { + apkLiteResult.getErrorMessage(), apkLiteResult.getException()); } - PackageLite pkgLite = new PackageLite(null, apkLiteResult.getResult(), null, null, + final ApkLite apkLite = apkLiteResult.getResult(); + PackageLite pkgLite = new PackageLite(null, apkLite.codePath, apkLite, null, null, null, null, null, null); sessionSize += PackageHelper.calculateInstalledSize(pkgLite, params.sessionParams.abiOverride, fd.getFileDescriptor()); @@ -2319,7 +2324,7 @@ class PackageManagerShellCommand extends ShellCommand { getErrPrintWriter().println("Error: no enforcement specified"); return 1; } - mPermissionManager.setPermissionEnforced(permission, Boolean.parseBoolean(enforcedRaw)); + // Permissions are always enforced now. return 0; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index f47b4b46fdd4..2d5034e624cd 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -362,8 +362,6 @@ public final class Settings { } } - Boolean mReadExternalStorageEnforced; - /** Device identity for the purpose of package verification. */ private VerifierDeviceIdentity mVerifierDeviceIdentity; @@ -475,6 +473,14 @@ public final class Settings { return mPackages.get(pkgName); } + ArrayMap<String, PackageSetting> getPackagesLocked() { + return mPackages; + } + + KeySetManagerService getKeySetManagerService() { + return mKeySetManagerService; + } + String getRenamedPackageLPr(String pkgName) { return mRenamedPackages.get(pkgName); } @@ -1969,28 +1975,28 @@ public final class Settings { serializer.attributeLong(null, ATTR_CE_DATA_INODE, ustate.ceDataInode); } if (!ustate.installed) { - serializer.attribute(null, ATTR_INSTALLED, "false"); + serializer.attributeBoolean(null, ATTR_INSTALLED, false); } if (ustate.stopped) { - serializer.attribute(null, ATTR_STOPPED, "true"); + serializer.attributeBoolean(null, ATTR_STOPPED, true); } if (ustate.notLaunched) { - serializer.attribute(null, ATTR_NOT_LAUNCHED, "true"); + serializer.attributeBoolean(null, ATTR_NOT_LAUNCHED, true); } if (ustate.hidden) { - serializer.attribute(null, ATTR_HIDDEN, "true"); + serializer.attributeBoolean(null, ATTR_HIDDEN, true); } if (ustate.distractionFlags != 0) { serializer.attributeInt(null, ATTR_DISTRACTION_FLAGS, ustate.distractionFlags); } if (ustate.suspended) { - serializer.attribute(null, ATTR_SUSPENDED, "true"); + serializer.attributeBoolean(null, ATTR_SUSPENDED, true); } if (ustate.instantApp) { - serializer.attribute(null, ATTR_INSTANT_APP, "true"); + serializer.attributeBoolean(null, ATTR_INSTANT_APP, true); } if (ustate.virtualPreload) { - serializer.attribute(null, ATTR_VIRTUAL_PRELOAD, "true"); + serializer.attributeBoolean(null, ATTR_VIRTUAL_PRELOAD, true); } if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { serializer.attributeInt(null, ATTR_ENABLED, ustate.enabled); @@ -2334,13 +2340,6 @@ public final class Settings { serializer.endTag(null, "verifier"); } - if (mReadExternalStorageEnforced != null) { - serializer.startTag(null, TAG_READ_EXTERNAL_STORAGE); - serializer.attribute( - null, ATTR_ENFORCEMENT, mReadExternalStorageEnforced ? "1" : "0"); - serializer.endTag(null, TAG_READ_EXTERNAL_STORAGE); - } - serializer.startTag(null, "permission-trees"); mPermissions.writePermissionTrees(serializer); serializer.endTag(null, "permission-trees"); @@ -2731,7 +2730,7 @@ public final class Settings { serializer.attributeInt(null, "sharedUserId", pkg.appId); } if (pkg.uidError) { - serializer.attribute(null, "uidError", "true"); + serializer.attributeBoolean(null, "uidError", true); } InstallSource installSource = pkg.installSource; if (installSource.installerPackageName != null) { @@ -2742,13 +2741,13 @@ public final class Settings { installSource.installerAttributionTag); } if (installSource.isOrphaned) { - serializer.attribute(null, "isOrphaned", "true"); + serializer.attributeBoolean(null, "isOrphaned", true); } if (installSource.initiatingPackageName != null) { serializer.attribute(null, "installInitiator", installSource.initiatingPackageName); } if (installSource.isInitiatingPackageUninstalled) { - serializer.attribute(null, "installInitiatorUninstalled", "true"); + serializer.attributeBoolean(null, "installInitiatorUninstalled", true); } if (installSource.originatingPackageName != null) { serializer.attribute(null, "installOriginator", installSource.originatingPackageName); @@ -2760,16 +2759,16 @@ public final class Settings { serializer.attributeInt(null, "categoryHint", pkg.categoryHint); } if (pkg.updateAvailable) { - serializer.attribute(null, "updateAvailable", "true"); + serializer.attributeBoolean(null, "updateAvailable", true); } if (pkg.forceQueryableOverride) { - serializer.attribute(null, "forceQueryable", "true"); + serializer.attributeBoolean(null, "forceQueryable", true); } if (pkg.isPackageStartable()) { - serializer.attribute(null, "isStartable", "true"); + serializer.attributeBoolean(null, "isStartable", true); } if (pkg.isPackageLoading()) { - serializer.attribute(null, "isLoading", "true"); + serializer.attributeBoolean(null, "isLoading", true); } writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions); @@ -2951,9 +2950,7 @@ public final class Settings { + e.getMessage()); } } else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) { - final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT); - mReadExternalStorageEnforced = - "1".equals(enforcement) ? Boolean.TRUE : Boolean.FALSE; + // No longer used. } else if (tagName.equals("keyset-settings")) { mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs); } else if (TAG_VERSION.equals(tagName)) { @@ -3500,14 +3497,14 @@ public final class Settings { String systemStr = null; String installerPackageName = null; String installerAttributionTag = null; - String isOrphaned = null; + boolean isOrphaned = false; String installOriginatingPackageName = null; String installInitiatingPackageName = null; - String installInitiatorUninstalled = null; + boolean installInitiatorUninstalled = false; String volumeUuid = null; - String updateAvailable = null; + boolean updateAvailable = false; int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; - String uidError = null; + boolean uidError = false; int pkgFlags = 0; int pkgPrivateFlags = 0; long timeStamp = 0; @@ -3515,14 +3512,14 @@ public final class Settings { long lastUpdateTime = 0; PackageSetting packageSetting = null; long versionCode = 0; - String installedForceQueryable = null; - String isStartable = null; - String isLoading = null; + boolean installedForceQueryable = false; + boolean isStartable = false; + boolean isLoading = false; try { name = parser.getAttributeValue(null, ATTR_NAME); realName = parser.getAttributeValue(null, "realName"); userId = parser.getAttributeInt(null, "userId", 0); - uidError = parser.getAttributeValue(null, "uidError"); + uidError = parser.getAttributeBoolean(null, "uidError", false); sharedUserId = parser.getAttributeInt(null, "sharedUserId", 0); codePathStr = parser.getAttributeValue(null, "codePath"); @@ -3532,10 +3529,10 @@ public final class Settings { primaryCpuAbiString = parser.getAttributeValue(null, "primaryCpuAbi"); secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi"); cpuAbiOverrideString = parser.getAttributeValue(null, "cpuAbiOverride"); - updateAvailable = parser.getAttributeValue(null, "updateAvailable"); - installedForceQueryable = parser.getAttributeValue(null, "forceQueryable"); - isStartable = parser.getAttributeValue(null, "isStartable"); - isLoading = parser.getAttributeValue(null, "isLoading"); + updateAvailable = parser.getAttributeBoolean(null, "updateAvailable", false); + installedForceQueryable = parser.getAttributeBoolean(null, "forceQueryable", false); + isStartable = parser.getAttributeBoolean(null, "isStartable", false); + isLoading = parser.getAttributeBoolean(null, "isLoading", false); if (primaryCpuAbiString == null && legacyCpuAbiString != null) { primaryCpuAbiString = legacyCpuAbiString; @@ -3544,11 +3541,11 @@ public final class Settings { versionCode = parser.getAttributeLong(null, "version", 0); installerPackageName = parser.getAttributeValue(null, "installer"); installerAttributionTag = parser.getAttributeValue(null, "installerAttributionTag"); - isOrphaned = parser.getAttributeValue(null, "isOrphaned"); + isOrphaned = parser.getAttributeBoolean(null, "isOrphaned", false); installInitiatingPackageName = parser.getAttributeValue(null, "installInitiator"); installOriginatingPackageName = parser.getAttributeValue(null, "installOriginator"); - installInitiatorUninstalled = parser.getAttributeValue(null, - "installInitiatorUninstalled"); + installInitiatorUninstalled = parser.getAttributeBoolean(null, + "installInitiatorUninstalled", false); volumeUuid = parser.getAttributeValue(null, "volumeUuid"); categoryHint = parser.getAttributeInt(null, "categoryHint", ApplicationInfo.CATEGORY_UNDEFINED); @@ -3670,21 +3667,20 @@ public final class Settings { + userId + " at " + parser.getPositionDescription()); } if (packageSetting != null) { - packageSetting.uidError = "true".equals(uidError); + packageSetting.uidError = uidError; InstallSource installSource = InstallSource.create( installInitiatingPackageName, installOriginatingPackageName, - installerPackageName, installerAttributionTag, "true".equals(isOrphaned), - "true".equals(installInitiatorUninstalled)); + installerPackageName, installerAttributionTag, isOrphaned, + installInitiatorUninstalled); packageSetting.installSource = installSource; packageSetting.volumeUuid = volumeUuid; packageSetting.categoryHint = categoryHint; packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr; packageSetting.primaryCpuAbiString = primaryCpuAbiString; packageSetting.secondaryCpuAbiString = secondaryCpuAbiString; - packageSetting.updateAvailable = "true".equals(updateAvailable); - packageSetting.forceQueryableOverride = "true".equals(installedForceQueryable); - packageSetting.incrementalStates = new IncrementalStates("true".equals(isStartable), - "true".equals(isLoading)); + packageSetting.updateAvailable = updateAvailable; + packageSetting.forceQueryableOverride = installedForceQueryable; + packageSetting.incrementalStates = new IncrementalStates(isStartable, isLoading); // Handle legacy string here for single-user mode final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); if (enabledStr != null) { @@ -3914,7 +3910,7 @@ public final class Settings { { name = parser.getAttributeValue(null, ATTR_NAME); int userId = parser.getAttributeInt(null, "userId", 0); - if ("true".equals(parser.getAttributeValue(null, "system"))) { + if (parser.getAttributeBoolean(null, "system", false)) { pkgFlags |= ApplicationInfo.FLAG_SYSTEM; } if (name == null) { @@ -4893,8 +4889,7 @@ public final class Settings { DumpState dumpState) { LegacyPermissionSettings.dumpPermissions(pw, packageName, permissionNames, mPermissionDataProvider.getLegacyPermissions(), - mPermissionDataProvider.getAllAppOpPermissionPackages(), - (mReadExternalStorageEnforced == Boolean.TRUE), dumpState); + mPermissionDataProvider.getAllAppOpPermissionPackages(), true, dumpState); } void dumpSharedUsersLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames, @@ -5554,7 +5549,8 @@ public final class Settings { return mPreferredActivities.get(userId); } - CrossProfileIntentResolver getCrossProfileIntentResolvers(int userId) { + @Nullable + CrossProfileIntentResolver getCrossProfileIntentResolver(int userId) { return mCrossProfileIntentResolvers.get(userId); } diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index 7d48d0a6e266..e913829ea9e2 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -30,6 +30,14 @@ ] }, { + "name": "FrameworksMockingServicesTests", + "options": [ + { + "include-filter": "com.android.server.pm." + } + ] + }, + { "name": "CtsContentTestCases", "options": [ { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index cc814bcc7760..62ac57062ac6 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2918,16 +2918,16 @@ public class UserManagerService extends IUserManager.Stub { serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath); } if (userInfo.partial) { - serializer.attribute(null, ATTR_PARTIAL, "true"); + serializer.attributeBoolean(null, ATTR_PARTIAL, true); } if (userInfo.preCreated) { - serializer.attribute(null, ATTR_PRE_CREATED, "true"); + serializer.attributeBoolean(null, ATTR_PRE_CREATED, true); } if (userInfo.convertedFromPreCreated) { - serializer.attribute(null, ATTR_CONVERTED_FROM_PRE_CREATED, "true"); + serializer.attributeBoolean(null, ATTR_CONVERTED_FROM_PRE_CREATED, true); } if (userInfo.guestToRemove) { - serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true"); + serializer.attributeBoolean(null, ATTR_GUEST_TO_REMOVE, true); } if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) { serializer.attributeInt(null, ATTR_PROFILE_GROUP_ID, userInfo.profileGroupId); @@ -3121,22 +3121,10 @@ public class UserManagerService extends IUserManager.Stub { profileBadge = parser.getAttributeInt(null, ATTR_PROFILE_BADGE, 0); restrictedProfileParentId = parser.getAttributeInt(null, ATTR_RESTRICTED_PROFILE_PARENT_ID, UserInfo.NO_PROFILE_GROUP_ID); - String valueString = parser.getAttributeValue(null, ATTR_PARTIAL); - if ("true".equals(valueString)) { - partial = true; - } - valueString = parser.getAttributeValue(null, ATTR_PRE_CREATED); - if ("true".equals(valueString)) { - preCreated = true; - } - valueString = parser.getAttributeValue(null, ATTR_CONVERTED_FROM_PRE_CREATED); - if ("true".equals(valueString)) { - converted = true; - } - valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE); - if ("true".equals(valueString)) { - guestToRemove = true; - } + partial = parser.getAttributeBoolean(null, ATTR_PARTIAL, false); + preCreated = parser.getAttributeBoolean(null, ATTR_PRE_CREATED, false); + converted = parser.getAttributeBoolean(null, ATTR_CONVERTED_FROM_PRE_CREATED, false); + guestToRemove = parser.getAttributeBoolean(null, ATTR_GUEST_TO_REMOVE, false); seedAccountName = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_NAME); seedAccountType = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_TYPE); @@ -4257,10 +4245,9 @@ public class UserManagerService extends IUserManager.Stub { if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) { String key = parser.getAttributeValue(null, ATTR_KEY); String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE); - String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE); - if (multiple != null) { + int count = parser.getAttributeInt(null, ATTR_MULTIPLE, -1); + if (count != -1) { values.clear(); - int count = Integer.parseInt(multiple); while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) { if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_VALUE)) { diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 0ac3030ba5dc..51dff4063850 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -344,7 +344,7 @@ public class UserRestrictionsUtils { } if (USER_RESTRICTIONS.contains(key)) { if (restrictions.getBoolean(key)) { - serializer.attribute(null, key, "true"); + serializer.attributeBoolean(null, key, true); } continue; } @@ -360,9 +360,9 @@ public class UserRestrictionsUtils { public static void readRestrictions(TypedXmlPullParser parser, Bundle restrictions) { restrictions.clear(); for (String key : USER_RESTRICTIONS) { - final String value = parser.getAttributeValue(null, key); - if (value != null) { - restrictions.putBoolean(key, Boolean.parseBoolean(value)); + final boolean value = parser.getAttributeBoolean(null, key, false); + if (value) { + restrictions.putBoolean(key, true); } } } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 165647205a98..44a2187aed8e 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -41,8 +41,9 @@ import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; -import android.os.Looper; +import android.os.HandlerThread; import android.os.Message; +import android.os.Process; import android.os.UserHandle; import android.os.storage.StorageManager; import android.permission.PermissionManager; @@ -67,8 +68,9 @@ import com.android.internal.R; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; -import com.android.server.pm.permission.PermissionManagerServiceInternal.PackagesProvider; -import com.android.server.pm.permission.PermissionManagerServiceInternal.SyncAdapterPackagesProvider; +import com.android.server.ServiceThread; +import com.android.server.pm.permission.LegacyPermissionManagerInternal.PackagesProvider; +import com.android.server.pm.permission.LegacyPermissionManagerInternal.SyncAdapterPackagesProvider; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -96,7 +98,7 @@ import java.util.Set; * to have an interface defined in the package manager but have the impl next to other * policy stuff like PhoneWindowManager */ -public final class DefaultPermissionGrantPolicy { +final class DefaultPermissionGrantPolicy { private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars private static final boolean DEBUG = false; @@ -291,9 +293,12 @@ public final class DefaultPermissionGrantPolicy { } }; - DefaultPermissionGrantPolicy(Context context, Looper looper) { + DefaultPermissionGrantPolicy(@NonNull Context context) { mContext = context; - mHandler = new Handler(looper) { + HandlerThread handlerThread = new ServiceThread(TAG, + Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); + handlerThread.start(); + mHandler = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) { @@ -998,12 +1003,6 @@ public final class DefaultPermissionGrantPolicy { } } - public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) { - Log.i(TAG, "Granting permissions to default browser for user:" + userId); - grantPermissionsToSystemPackage(NO_PM_CACHE, packageName, userId, - FOREGROUND_LOCATION_PERMISSIONS); - } - private String getDefaultSystemHandlerActivityPackage(PackageManagerWrapper pm, String intentAction, int userId) { return getDefaultSystemHandlerActivityPackage(pm, new Intent(intentAction), userId); diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermission.java b/services/core/java/com/android/server/pm/permission/LegacyPermission.java index 6a4eb63c8d7e..ca3a2e2e2da7 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermission.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermission.java @@ -167,16 +167,12 @@ public final class LegacyPermission { private static int readInt(@NonNull TypedXmlPullParser parser, @Nullable String namespace, @NonNull String name, int defaultValue) { - final String value = parser.getAttributeValue(namespace, name); - if (value == null) { - return defaultValue; - } try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { + return parser.getAttributeInt(namespace, name); + } catch (Exception ignored) { PackageManagerService.reportSettingsProblem(Log.WARN, "Error in package manager settings: attribute " + name - + " has bad integer value " + value + " at " + + " has bad integer value at " + parser.getPositionDescription()); return defaultValue; } diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java new file mode 100644 index 000000000000..a098484b803b --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java @@ -0,0 +1,172 @@ +/* + * 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.pm.permission; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; + +/** + * The internal interface for {@link LegacyPermissionManagerService}. + */ +public interface LegacyPermissionManagerInternal { + /** + * Sets the dialer application packages provider. + * @param provider The provider. + */ + void setDialerAppPackagesProvider(PackagesProvider provider); + + /** + * Set the location extra packages provider. + * @param provider The packages provider. + */ + void setLocationExtraPackagesProvider(PackagesProvider provider); + + /** + * Sets the location provider packages provider. + * @param provider The packages provider. + */ + void setLocationPackagesProvider(PackagesProvider provider); + + /** + * Sets the SIM call manager packages provider. + * @param provider The provider. + */ + void setSimCallManagerPackagesProvider(PackagesProvider provider); + + /** + * Sets the SMS application packages provider. + * @param provider The provider. + */ + void setSmsAppPackagesProvider(PackagesProvider provider); + + /** + * Sets the sync adapter packages provider. + * @param provider The provider. + */ + void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider); + + /** + * Sets the Use Open Wifi packages provider. + * @param provider The packages provider. + */ + void setUseOpenWifiAppPackagesProvider(PackagesProvider provider); + + /** + * Sets the voice interaction packages provider. + * @param provider The packages provider. + */ + void setVoiceInteractionPackagesProvider(PackagesProvider provider); + + /** + * Requests granting of the default permissions to the current default Use Open Wifi app. + * @param packageName The default use open wifi package name. + * @param userId The user for which to grant the permissions. + */ + void grantDefaultPermissionsToDefaultSimCallManager(@NonNull String packageName, + @UserIdInt int userId); + + /** + * Requests granting of the default permissions to the current default Use Open Wifi app. + * @param packageName The default use open wifi package name. + * @param userId The user for which to grant the permissions. + */ + void grantDefaultPermissionsToDefaultUseOpenWifiApp(@NonNull String packageName, + @UserIdInt int userId); + + /** + * Grant the default permissions for a user. + * + * @param userId the user ID + */ + void grantDefaultPermissions(@UserIdInt int userId); + + /** + * Schedule reading the default permission exceptions file. + */ + void scheduleReadDefaultPermissionExceptions(); + + // TODO(zhanghai): The following methods should be moved to a new AIDL to support + // the legacy PermissionManager directly in a later CL. + + /** + * Grant default permissions to currently active LUI app + * @param packageName The package name for the LUI app + * @param userId The user ID + */ + void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId); + + /** + * Revoke default permissions to currently active LUI app + * @param packageNames The package names for the LUI apps + * @param userId The user ID + */ + void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId); + + /** + * Grant default permissions to currently active Ims services + * @param packageNames The package names for the Ims services + * @param userId The user ID + */ + void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId); + + /** + * Grant default permissions to currently enabled telephony data services + * @param packageNames The package name for the services + * @param userId The user ID + */ + void grantDefaultPermissionsToEnabledTelephonyDataServices(String[] packageNames, int userId); + + /** + * Revoke default permissions to currently active telephony data services + * @param packageNames The package name for the services + * @param userId The IDhandle + */ + void revokeDefaultPermissionsFromDisabledTelephonyDataServices(String[] packageNames, + int userId); + + /** + * Grant default permissions to currently enabled carrier apps + * @param packageNames Package names of the apps to be granted permissions + * @param userId The user ID + */ + void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId); + + /** + * Provider for package names. + */ + interface PackagesProvider { + /** + * Gets the packages for a given user. + * @param userId The user id. + * @return The package names. + */ + String[] getPackages(int userId); + } + + /** + * Provider for package names. + */ + interface SyncAdapterPackagesProvider { + /** + * Gets the sync adapter packages for given authority and user. + * @param authority The authority. + * @param userId The user id. + * @return The package names. + */ + String[] getPackages(String authority, int userId); + } +} diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java new file mode 100644 index 000000000000..0c0a8dfeaaec --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java @@ -0,0 +1,181 @@ +/* + * 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.pm.permission; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.Binder; + +import com.android.server.LocalServices; +import com.android.server.pm.PackageManagerServiceUtils; + +/** + * Legacy permission manager service. + */ +public class LegacyPermissionManagerService { + @NonNull + private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy; + + /** + * Get or create an instance of this class for use by other components. + * <p> + * This method is not thread-safe. + * + * @param context the {@link Context} + * @return the internal instance + */ + @NonNull + public static LegacyPermissionManagerInternal create(@NonNull Context context) { + LegacyPermissionManagerInternal legacyPermissionManagerInternal = LocalServices.getService( + LegacyPermissionManagerInternal.class); + if (legacyPermissionManagerInternal == null) { + new LegacyPermissionManagerService(context); + legacyPermissionManagerInternal = LocalServices.getService( + LegacyPermissionManagerInternal.class); + } + return legacyPermissionManagerInternal; + } + + private LegacyPermissionManagerService(@NonNull Context context) { + mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(context); + LocalServices.addService(LegacyPermissionManagerInternal.class, new Internal()); + } + + private class Internal implements LegacyPermissionManagerInternal { + @Override + public void setDialerAppPackagesProvider(PackagesProvider provider) { + mDefaultPermissionGrantPolicy.setDialerAppPackagesProvider(provider); + } + + @Override + public void setLocationExtraPackagesProvider(PackagesProvider provider) { + mDefaultPermissionGrantPolicy.setLocationExtraPackagesProvider(provider); + } + + @Override + public void setLocationPackagesProvider(PackagesProvider provider) { + mDefaultPermissionGrantPolicy.setLocationPackagesProvider(provider); + } + + @Override + public void setSimCallManagerPackagesProvider(PackagesProvider provider) { + mDefaultPermissionGrantPolicy.setSimCallManagerPackagesProvider(provider); + } + + @Override + public void setSmsAppPackagesProvider(PackagesProvider provider) { + mDefaultPermissionGrantPolicy.setSmsAppPackagesProvider(provider); + } + + @Override + public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) { + mDefaultPermissionGrantPolicy.setSyncAdapterPackagesProvider(provider); + } + + @Override + public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) { + mDefaultPermissionGrantPolicy.setUseOpenWifiAppPackagesProvider(provider); + } + + @Override + public void setVoiceInteractionPackagesProvider(PackagesProvider provider) { + mDefaultPermissionGrantPolicy.setVoiceInteractionPackagesProvider(provider); + } + + @Override + public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) { + mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultSimCallManager( + packageName, userId); + } + + @Override + public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) { + mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultUseOpenWifiApp( + packageName, userId); + } + + @Override + public void grantDefaultPermissions(int userId) { + mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId); + } + + @Override + public void scheduleReadDefaultPermissionExceptions() { + mDefaultPermissionGrantPolicy.scheduleReadDefaultPermissionExceptions(); + } + + // TODO(zhanghai): The following methods should be moved to a new AIDL to support + // the legacy PermissionManager directly in a later CL. + + @Override + public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils.enforceSystemOrPhoneCaller( + "grantDefaultPermissionsToActiveLuiApp", callingUid); + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToActiveLuiApp(packageName, userId)); + } + + @Override + public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils.enforceSystemOrPhoneCaller( + "revokeDefaultPermissionsFromLuiApps", callingUid); + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .revokeDefaultPermissionsFromLuiApps(packageNames, userId)); + } + + @Override + public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils.enforceSystemOrPhoneCaller( + "grantDefaultPermissionsToEnabledImsServices", callingUid); + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToEnabledImsServices(packageNames, userId)); + } + + @Override + public void grantDefaultPermissionsToEnabledTelephonyDataServices( + String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils.enforceSystemOrPhoneCaller( + "grantDefaultPermissionsToEnabledTelephonyDataServices", callingUid); + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToEnabledTelephonyDataServices(packageNames, userId)); + } + + @Override + public void revokeDefaultPermissionsFromDisabledTelephonyDataServices( + String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils.enforceSystemOrPhoneCaller( + "revokeDefaultPermissionsFromDisabledTelephonyDataServices", callingUid); + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .revokeDefaultPermissionsFromDisabledTelephonyDataServices(packageNames, + userId)); + } + + @Override + public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils.enforceSystemOrPhoneCaller( + "grantPermissionsToEnabledCarrierApps", callingUid); + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId)); + } + } +} diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 52bb3d772387..791efd022fe9 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -114,8 +114,6 @@ import android.permission.IPermissionManager; import android.permission.PermissionControllerManager; import android.permission.PermissionManager; import android.permission.PermissionManagerInternal; -import android.permission.PermissionManagerInternal.CheckPermissionDelegate; -import android.permission.PermissionManagerInternal.OnRuntimePermissionStateChangedListener; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -138,6 +136,7 @@ import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IntPair; import com.android.internal.util.Preconditions; +import com.android.internal.util.function.TriFunction; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; @@ -145,16 +144,12 @@ import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.Watchdog; import com.android.server.pm.ApexManager; -import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultBrowserProvider; -import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider; -import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider; -import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback; +import com.android.server.pm.permission.PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.SoftRestrictedPermissionPolicy; @@ -177,7 +172,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; +import java.util.function.BiFunction; /** * Manages all permissions and handles permissions related tasks. @@ -243,9 +238,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { private final SparseArray<OneTimePermissionUserManager> mOneTimePermissionUserManagers = new SparseArray<>(); - /** Default permission policy to provide proper behaviour out-of-the-box */ - private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy; - /** App ops manager */ private final AppOpsManager mAppOpsManager; @@ -305,15 +297,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { @NonNull private final OnPermissionChangeListeners mOnPermissionChangeListeners; - @GuardedBy("mLock") - private DefaultBrowserProvider mDefaultBrowserProvider; - - @GuardedBy("mLock") - private DefaultDialerProvider mDefaultDialerProvider; - - @GuardedBy("mLock") - private DefaultHomeProvider mDefaultHomeProvider; - // TODO: Take a look at the methods defined in the callback. // The callback was initially created to support the split between permission // manager and the package manager. However, it's started to be used for other @@ -403,8 +386,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { mHandler = new Handler(mHandlerThread.getLooper()); Watchdog.getInstance().addThread(mHandler); - mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy( - context, mHandlerThread.getLooper()); SystemConfig systemConfig = SystemConfig.getInstance(); mSystemPermissions = systemConfig.getSystemPermissions(); mGlobalGids = systemConfig.getGlobalGids(); @@ -2019,145 +2000,41 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public String getDefaultBrowser(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getUserId(callingUid) != userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - } - if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { - return null; - } - DefaultBrowserProvider provider; - synchronized (mLock) { - provider = mDefaultBrowserProvider; - } - return provider != null ? provider.getDefaultBrowser(userId) : null; - } - - @Override - public boolean setDefaultBrowser(String packageName, int userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - } - return setDefaultBrowserInternal(packageName, false, true, userId); - } - - private boolean setDefaultBrowserInternal(String packageName, boolean async, - boolean doGrant, int userId) { - if (userId == UserHandle.USER_ALL) { - return false; - } - DefaultBrowserProvider provider; - synchronized (mLock) { - provider = mDefaultBrowserProvider; - } - if (provider == null) { - return false; - } - if (async) { - provider.setDefaultBrowserAsync(packageName, userId); - } else { - if (!provider.setDefaultBrowser(packageName, userId)) { - return false; - } - } - if (doGrant && packageName != null) { - mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultBrowser(packageName, - userId); - } - return true; - } - - @Override public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) { - final int callingUid = Binder.getCallingUid(); - PackageManagerServiceUtils - .enforceSystemOrPhoneCaller("grantPermissionsToEnabledCarrierApps", callingUid); - Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy - .grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId)); + LocalServices.getService(LegacyPermissionManagerInternal.class) + .grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId); } @Override public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) { - final int callingUid = Binder.getCallingUid(); - PackageManagerServiceUtils.enforceSystemOrPhoneCaller( - "grantDefaultPermissionsToEnabledImsServices", callingUid); - Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy - .grantDefaultPermissionsToEnabledImsServices(packageNames, userId)); + LocalServices.getService(LegacyPermissionManagerInternal.class) + .grantDefaultPermissionsToEnabledImsServices(packageNames, userId); } @Override public void grantDefaultPermissionsToEnabledTelephonyDataServices( String[] packageNames, int userId) { - final int callingUid = Binder.getCallingUid(); - PackageManagerServiceUtils.enforceSystemOrPhoneCaller( - "grantDefaultPermissionsToEnabledTelephonyDataServices", callingUid); - Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy - .grantDefaultPermissionsToEnabledTelephonyDataServices(packageNames, userId)); + LocalServices.getService(LegacyPermissionManagerInternal.class) + .grantDefaultPermissionsToEnabledTelephonyDataServices(packageNames, userId); } @Override public void revokeDefaultPermissionsFromDisabledTelephonyDataServices( String[] packageNames, int userId) { - final int callingUid = Binder.getCallingUid(); - PackageManagerServiceUtils.enforceSystemOrPhoneCaller( - "revokeDefaultPermissionsFromDisabledTelephonyDataServices", callingUid); - Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy - .revokeDefaultPermissionsFromDisabledTelephonyDataServices(packageNames, userId)); + LocalServices.getService(LegacyPermissionManagerInternal.class) + .revokeDefaultPermissionsFromDisabledTelephonyDataServices(packageNames, userId); } @Override public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) { - final int callingUid = Binder.getCallingUid(); - PackageManagerServiceUtils - .enforceSystemOrPhoneCaller("grantDefaultPermissionsToActiveLuiApp", callingUid); - Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy - .grantDefaultPermissionsToActiveLuiApp(packageName, userId)); + LocalServices.getService(LegacyPermissionManagerInternal.class) + .grantDefaultPermissionsToActiveLuiApp(packageName, userId); } @Override public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) { - final int callingUid = Binder.getCallingUid(); - PackageManagerServiceUtils - .enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromLuiApps", callingUid); - Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy - .revokeDefaultPermissionsFromLuiApps(packageNames, userId)); - } - - @Override - public void setPermissionEnforced(String permName, boolean enforced) { - // TODO: Now that we no longer change GID for storage, this should to away. - mContext.enforceCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, - "setPermissionEnforced"); - if (READ_EXTERNAL_STORAGE.equals(permName)) { - mPackageManagerInt.setReadExternalStorageEnforced(enforced); - // kill any non-foreground processes so we restart them and - // grant/revoke the GID. - final IActivityManager am = ActivityManager.getService(); - if (am != null) { - final long token = Binder.clearCallingIdentity(); - try { - am.killProcessesBelowForeground("setPermissionEnforcement"); - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(token); - } - } - } else { - throw new IllegalArgumentException("No selective enforcement for " + permName); - } - } - - /** @deprecated */ - @Override - @Deprecated - public boolean isPermissionEnforced(String permName) { - // allow instant applications - return true; + LocalServices.getService(LegacyPermissionManagerInternal.class) + .revokeDefaultPermissionsFromLuiApps(packageNames, userId); } /** @@ -2264,19 +2141,20 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * <p>Can not be called on main thread. * - * @param user The user the data should be extracted for + * @param userId The user ID the data should be extracted for * * @return The state as a xml file */ - private @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) { + @Nullable + private byte[] backupRuntimePermissions(@UserIdInt int userId) { CompletableFuture<byte[]> backup = new CompletableFuture<>(); - mPermissionControllerManager.getRuntimePermissionBackup(user, mContext.getMainExecutor(), - backup::complete); + mPermissionControllerManager.getRuntimePermissionBackup(UserHandle.of(userId), + mContext.getMainExecutor(), backup::complete); try { return backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { - Slog.e(TAG, "Cannot create permission backup for " + user, e); + Slog.e(TAG, "Cannot create permission backup for user " + userId, e); return null; } } @@ -2288,13 +2166,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { * applied via {@link #restoreDelayedRuntimePermissions}. * * @param backup The state as an xml file - * @param user The user the data should be restored for + * @param userId The user ID the data should be restored for */ - private void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) { + private void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId) { synchronized (mLock) { - mHasNoDelayedPermBackup.delete(user.getIdentifier()); + mHasNoDelayedPermBackup.delete(userId); } - mPermissionControllerManager.stageAndApplyRuntimePermissionsBackup(backup, user); + mPermissionControllerManager.stageAndApplyRuntimePermissionsBackup(backup, + UserHandle.of(userId)); } /** @@ -2303,24 +2182,24 @@ public class PermissionManagerService extends IPermissionManager.Stub { * <p>Can not be called on main thread. * * @param packageName The package that is newly installed - * @param user The user the package is installed for + * @param userId The user ID the package is installed for * * @see #restoreRuntimePermissions */ private void restoreDelayedRuntimePermissions(@NonNull String packageName, - @NonNull UserHandle user) { + @UserIdInt int userId) { synchronized (mLock) { - if (mHasNoDelayedPermBackup.get(user.getIdentifier(), false)) { + if (mHasNoDelayedPermBackup.get(userId, false)) { return; } } - mPermissionControllerManager.applyStagedRuntimePermissionBackup(packageName, user, - mContext.getMainExecutor(), (hasMoreBackup) -> { + mPermissionControllerManager.applyStagedRuntimePermissionBackup(packageName, + UserHandle.of(userId), mContext.getMainExecutor(), (hasMoreBackup) -> { if (hasMoreBackup) { return; } synchronized (mLock) { - mHasNoDelayedPermBackup.put(user.getIdentifier(), true); + mHasNoDelayedPermBackup.put(userId, true); } }); } @@ -2361,6 +2240,32 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + private void startShellPermissionIdentityDelegationInternal(int uid, + @NonNull String packageName, @Nullable List<String> permissionNames) { + synchronized (mLock) { + final CheckPermissionDelegate oldDelegate = mCheckPermissionDelegate; + if (oldDelegate != null && oldDelegate.getDelegatedUid() != uid) { + throw new SecurityException( + "Shell can delegate permissions only to one UID at a time"); + } + final ShellDelegate delegate = new ShellDelegate(uid, packageName, permissionNames); + setCheckPermissionDelegateLocked(delegate); + } + } + + private void stopShellPermissionIdentityDelegationInternal() { + synchronized (mLock) { + setCheckPermissionDelegateLocked(null); + } + } + + private void setCheckPermissionDelegateLocked(@Nullable CheckPermissionDelegate delegate) { + if (delegate != null || mCheckPermissionDelegate != null) { + PackageManager.invalidatePackageInfoCache(); + } + mCheckPermissionDelegate = delegate; + } + /** * If the app is updated, and has scoped storage permissions, then it is possible that the * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions. @@ -4139,17 +4044,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * @param packageName The package that is updated * @param pkg The package that is updated, or {@code null} if package is deleted - * @param allPackages All currently known packages - * @param callback Callback to call after permission changes */ - private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg, - @NonNull PermissionCallback callback) { + private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) { // If the package is being deleted, update the permissions of all the apps final int flags = (pkg == null ? UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG : UPDATE_PERMISSIONS_REPLACE_PKG); updatePermissions( - packageName, pkg, getVolumeUuidForPackage(pkg), flags, callback); + packageName, pkg, getVolumeUuidForPackage(pkg), flags, mDefaultPermissionCallback); } /** @@ -4625,24 +4527,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class); - - int[] grantPermissionsUserIds = EMPTY_INT_ARRAY; - for (int userId : UserManagerService.getInstance().getUserIds()) { - if (mPackageManagerInt.isPermissionUpgradeNeeded(userId)) { - grantPermissionsUserIds = ArrayUtils.appendInt( - grantPermissionsUserIds, userId); - } - } - // If we upgraded grant all default permissions before kicking off. - for (int userId : grantPermissionsUserIds) { - mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId); - } - if (grantPermissionsUserIds == EMPTY_INT_ARRAY) { - // If we did not grant default permissions, we preload from this the - // default permission exceptions lazily to ensure we don't hit the - // disk on a new user creation. - mDefaultPermissionGrantPolicy.scheduleReadDefaultPermissionExceptions(); - } } private static String getVolumeUuidForPackage(AndroidPackage pkg) { @@ -4712,13 +4596,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { return userState.getUidState(appId); } - private void removeAppIdState(@AppIdInt int appId) { + private void removeUidState(@AppIdInt int appId, @UserIdInt int userId) { synchronized (mLock) { - final int[] userIds = mState.getUserIds(); - for (final int userId : userIds) { - final UserPermissionState userState = mState.getUserState(userId); - userState.removeUidState(appId); + final UserPermissionState userState = mState.getUserState(userId); + if (userState == null) { + return; } + userState.removeUidState(appId); } } @@ -4947,17 +4831,19 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, - @NonNull List<String> grantedPermissions, - @NonNull List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode, + @NonNull PermissionManagerServiceInternal.PackageInstalledParams params, @UserIdInt int userId) { - addAllowlistedRestrictedPermissionsInternal(pkg, allowlistedRestrictedPermissions, + updatePermissions(pkg.getPackageName(), pkg); + addAllowlistedRestrictedPermissionsInternal(pkg, + params.getAllowlistedRestrictedPermissions(), FLAG_PERMISSION_WHITELIST_INSTALLER, userId); + final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode(); if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) { setAutoRevokeExemptedInternal(pkg, autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId); } - grantRequestedRuntimePermissionsInternal(pkg, grantedPermissions, userId); + grantRequestedRuntimePermissionsInternal(pkg, params.getGrantedPermissions(), userId); } private void addAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, @@ -4978,25 +4864,34 @@ public class PermissionManagerService extends IPermissionManager.Stub { removeAllPermissionsInternal(pkg); } - private void onPackageStateRemovedInternal(@NonNull String packageName, int appId, - @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs) { - if (sharedUserPkgs.isEmpty() - && mPackageManagerInt.getDisabledSystemPackage(packageName) == null) { - removeAppIdState(appId); + private void onPackageUninstalledInternal(@NonNull String packageName, int appId, + @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs, + @UserIdInt int userId) { + // TODO: Move these checks to check PackageState to be more reliable. + // System packages should always have an available APK. + if (pkg != null && pkg.isSystem() + // We may be fully removing invalid system packages during boot, and in that case we + // do want to remove their permission state. So make sure that the package is only + // being marked as uninstalled instead of fully removed. + && mPackageManagerInt.getPackage(packageName) != null) { + // If we are only marking a system package as uninstalled, we need to keep its + // pregranted permission state so that it still works once it gets reinstalled, thus + // only reset the user modifications to its permission state. + resetRuntimePermissionsInternal(pkg, userId); + return; } - updatePermissions(packageName, null, mDefaultPermissionCallback); - if (!sharedUserPkgs.isEmpty()) { + updatePermissions(packageName, null); + if (sharedUserPkgs.isEmpty()) { + removeUidState(appId, userId); + } else { // Remove permissions associated with package. Since runtime // permissions are per user we have to kill the removed package // or packages running under the shared user of the removed // package if revoking the permissions requested only by the removed // package is successful and this causes a change in gids. - boolean shouldKill = false; - for (int userId : UserManagerService.getInstance().getUserIds()) { - final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg, - sharedUserPkgs, userId); - shouldKill |= userIdToKill != UserHandle.USER_NULL; - } + final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg, + sharedUserPkgs, userId); + final boolean shouldKill = userIdToKill != UserHandle.USER_NULL; // If gids changed, kill all affected packages. if (shouldKill) { mHandler.post(() -> { @@ -5091,7 +4986,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal { + private class PermissionManagerServiceInternalImpl implements PermissionManagerServiceInternal { @Override public void systemReady() { PermissionManagerService.this.systemReady(); @@ -5116,6 +5011,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override public void onUserRemoved(@UserIdInt int userId) { + Preconditions.checkArgumentNonNegative(userId, "userId"); PermissionManagerService.this.onUserRemoved(userId); } @NonNull @@ -5140,11 +5036,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PermissionManagerService.this.getAppOpPermissionPackagesInternal(permissionName); } @Override - public void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) { - PermissionManagerService.this - .updatePermissions(packageName, pkg, mDefaultPermissionCallback); - } - @Override public void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated) { PermissionManagerService.this .updateAllPermissions(volumeUuid, sdkUpdated, mDefaultPermissionCallback); @@ -5200,20 +5091,26 @@ public class PermissionManagerService extends IPermissionManager.Stub { return matchingPermissions; } + @Nullable @Override - public @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) { - return PermissionManagerService.this.backupRuntimePermissions(user); + public byte[] backupRuntimePermissions(@UserIdInt int userId) { + Preconditions.checkArgumentNonNegative(userId, "userId"); + return PermissionManagerService.this.backupRuntimePermissions(userId); } @Override - public void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) { - PermissionManagerService.this.restoreRuntimePermissions(backup, user); + public void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId) { + Objects.requireNonNull(backup, "backup"); + Preconditions.checkArgumentNonNegative(userId, "userId"); + PermissionManagerService.this.restoreRuntimePermissions(backup, userId); } @Override public void restoreDelayedRuntimePermissions(@NonNull String packageName, - @NonNull UserHandle user) { - PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, user); + @UserIdInt int userId) { + Objects.requireNonNull(packageName, "packageName"); + Preconditions.checkArgumentNonNegative(userId, "userId"); + PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, userId); } @Override @@ -5231,149 +5128,23 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public CheckPermissionDelegate getCheckPermissionDelegate() { - synchronized (mLock) { - return mCheckPermissionDelegate; - } - } - - @Override - public void setCheckPermissionDelegate(CheckPermissionDelegate delegate) { - synchronized (mLock) { - if (delegate != null || mCheckPermissionDelegate != null) { - PackageManager.invalidatePackageInfoCache(); - } - mCheckPermissionDelegate = delegate; - } - } - - @Override - public void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider) { - synchronized (mLock) { - mDefaultBrowserProvider = provider; - } - } - - @Override - public void setDefaultBrowser(String packageName, boolean async, boolean doGrant, - int userId) { - setDefaultBrowserInternal(packageName, async, doGrant, userId); - } - - @Override - public void setDefaultDialerProvider(@NonNull DefaultDialerProvider provider) { - synchronized (mLock) { - mDefaultDialerProvider = provider; - } - } - - @Override - public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) { - synchronized (mLock) { - mDefaultHomeProvider = provider; - } - } - - @Override - public void setDefaultHome(String packageName, int userId, Consumer<Boolean> callback) { - if (userId == UserHandle.USER_ALL) { - return; - } - DefaultHomeProvider provider; - synchronized (mLock) { - provider = mDefaultHomeProvider; - } - if (provider == null) { - return; - } - provider.setDefaultHomeAsync(packageName, userId, callback); - } - - @Override - public void setDialerAppPackagesProvider(PackagesProvider provider) { - mDefaultPermissionGrantPolicy.setDialerAppPackagesProvider(provider); - } - - @Override - public void setLocationExtraPackagesProvider(PackagesProvider provider) { - mDefaultPermissionGrantPolicy.setLocationExtraPackagesProvider(provider); - } - - @Override - public void setLocationPackagesProvider(PackagesProvider provider) { - mDefaultPermissionGrantPolicy.setLocationPackagesProvider(provider); - } - - @Override - public void setSimCallManagerPackagesProvider(PackagesProvider provider) { - mDefaultPermissionGrantPolicy.setSimCallManagerPackagesProvider(provider); - } - - @Override - public void setSmsAppPackagesProvider(PackagesProvider provider) { - mDefaultPermissionGrantPolicy.setSmsAppPackagesProvider(provider); - } - - @Override - public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) { - mDefaultPermissionGrantPolicy.setSyncAdapterPackagesProvider(provider); - } - - @Override - public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) { - mDefaultPermissionGrantPolicy.setUseOpenWifiAppPackagesProvider(provider); - } - - @Override - public void setVoiceInteractionPackagesProvider(PackagesProvider provider) { - mDefaultPermissionGrantPolicy.setVoiceInteractionPackagesProvider(provider); - } - - @Override - public String getDefaultBrowser(int userId) { - DefaultBrowserProvider provider; - synchronized (mLock) { - provider = mDefaultBrowserProvider; - } - return provider != null ? provider.getDefaultBrowser(userId) : null; - } - - @Override - public String getDefaultDialer(int userId) { - DefaultDialerProvider provider; - synchronized (mLock) { - provider = mDefaultDialerProvider; - } - return provider != null ? provider.getDefaultDialer(userId) : null; - } - - @Override - public String getDefaultHome(int userId) { - DefaultHomeProvider provider; - synchronized (mLock) { - provider = mDefaultHomeProvider; - } - return provider != null ? provider.getDefaultHome(userId) : null; - } - - @Override - public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) { - mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultSimCallManager( - packageName, userId); + public void startShellPermissionIdentityDelegation(int uid, @NonNull String packageName, + @Nullable List<String> permissionNames) { + Objects.requireNonNull(packageName, "packageName"); + startShellPermissionIdentityDelegationInternal(uid, packageName, permissionNames); } @Override - public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) { - mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultUseOpenWifiApp( - packageName, userId); + public void stopShellPermissionIdentityDelegation() { + stopShellPermissionIdentityDelegationInternal(); } @Override - public void onNewUserCreated(int userId) { + public void onUserCreated(@UserIdInt int userId) { + Preconditions.checkArgumentNonNegative(userId, "userId"); // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG PermissionManagerService.this.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, true, mDefaultPermissionCallback); - mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId); } @Override @@ -5411,16 +5182,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public void onPackageInstalled(@NonNull AndroidPackage pkg, - @NonNull List<String> grantedPermissions, - @NonNull List<String> allowlistedRestrictedPermissions, - int autoRevokePermissionsMode, @UserIdInt int userId) { + @NonNull PackageInstalledParams params, @UserIdInt int userId) { Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(grantedPermissions, "grantedPermissions"); - Objects.requireNonNull(allowlistedRestrictedPermissions, - "allowlistedRestrictedPermissions"); + Objects.requireNonNull(params, "params"); Preconditions.checkArgumentNonNegative(userId, "userId"); - onPackageInstalledInternal(pkg, grantedPermissions, allowlistedRestrictedPermissions, - autoRevokePermissionsMode, userId); + onPackageInstalledInternal(pkg, params, userId); } @Override @@ -5430,11 +5196,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void onPackageStateRemoved(@NonNull String packageName, int appId, - @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(sharedUserPkgs); - onPackageStateRemovedInternal(packageName, appId, pkg, sharedUserPkgs); + public void onPackageUninstalled(@NonNull String packageName, int appId, + @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs, + @UserIdInt int userId) { + Objects.requireNonNull(packageName, "packageName"); + Objects.requireNonNull(sharedUserPkgs, "sharedUserPkgs"); + Preconditions.checkArgumentNonNegative(userId, "userId"); + onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userId); } @Override @@ -5467,6 +5235,32 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + /** + * Callbacks invoked when interesting actions have been taken on a permission. + * <p> + * NOTE: The current arguments are merely to support the existing use cases. This + * needs to be properly thought out with appropriate arguments for each of the + * callback methods. + */ + private static class PermissionCallback { + public void onGidsChanged(@AppIdInt int appId, @UserIdInt int userId) {} + public void onPermissionChanged() {} + public void onPermissionGranted(int uid, @UserIdInt int userId) {} + public void onInstallPermissionGranted() {} + public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {} + public void onInstallPermissionRevoked() {} + public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) {} + public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync, + int uid) { + onPermissionUpdated(updatedUserIds, sync); + } + public void onPermissionRemoved() {} + public void onInstallPermissionUpdated() {} + public void onInstallPermissionUpdatedNotifyListener(int uid) { + onInstallPermissionUpdated(); + } + } + private static final class OnPermissionChangeListeners extends Handler { private static final int MSG_ON_PERMISSIONS_CHANGED = 1; @@ -5520,6 +5314,102 @@ public class PermissionManagerService extends IPermissionManager.Stub { } /** + * Interface to intercept permission checks and optionally pass through to the original + * implementation. + */ + private interface CheckPermissionDelegate { + /** + * Get the UID whose permission checks is being delegated. + * + * @return the UID + */ + int getDelegatedUid(); + + /** + * Check whether the given package has been granted the specified permission. + * + * @param permissionName the name of the permission to be checked + * @param packageName the name of the package to be checked + * @param userId the user ID + * @param superImpl the original implementation that can be delegated to + * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has + * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise + * + * @see android.content.pm.PackageManager#checkPermission(String, String) + */ + int checkPermission(@NonNull String permissionName, @NonNull String packageName, + @UserIdInt int userId, + @NonNull TriFunction<String, String, Integer, Integer> superImpl); + + /** + * Check whether the given UID has been granted the specified permission. + * + * @param permissionName the name of the permission to be checked + * @param uid the UID to be checked + * @param superImpl the original implementation that can be delegated to + * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has + * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise + */ + int checkUidPermission(@NonNull String permissionName, int uid, + BiFunction<String, Integer, Integer> superImpl); + } + + private class ShellDelegate implements CheckPermissionDelegate { + private final int mDelegatedUid; + @NonNull + private final String mDelegatedPackageName; + @Nullable + private final List<String> mDelegatedPermissionNames; + + public ShellDelegate(int delegatedUid, @NonNull String delegatedPackageName, + @Nullable List<String> delegatedPermissionNames) { + mDelegatedUid = delegatedUid; + mDelegatedPackageName = delegatedPackageName; + mDelegatedPermissionNames = delegatedPermissionNames; + } + + @Override + public int getDelegatedUid() { + return mDelegatedUid; + } + + @Override + public int checkPermission(@NonNull String permissionName, @NonNull String packageName, + int userId, @NonNull TriFunction<String, String, Integer, Integer> superImpl) { + if (mDelegatedPackageName.equals(packageName) + && isDelegatedPermission(permissionName)) { + final long identity = Binder.clearCallingIdentity(); + try { + return superImpl.apply(permissionName, "com.android.shell", userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + return superImpl.apply(permissionName, packageName, userId); + } + + @Override + public int checkUidPermission(@NonNull String permissionName, int uid, + @NonNull BiFunction<String, Integer, Integer> superImpl) { + if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) { + final long identity = Binder.clearCallingIdentity(); + try { + return superImpl.apply(permissionName, Process.SHELL_UID); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + return superImpl.apply(permissionName, uid); + } + + private boolean isDelegatedPermission(@NonNull String permissionName) { + // null permissions means all permissions are targeted + return mDelegatedPermissionNames == null + || mDelegatedPermissionNames.contains(permissionName); + } + } + + /** * Allows injection of services and method responses to facilitate testing. * * <p>Test classes can create a mock of this class and pass it to the PermissionManagerService diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 457fe36ca2b8..66e692db2154 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -16,166 +16,45 @@ package com.android.server.pm.permission; -import android.annotation.AppIdInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.AppOpsManager; import android.content.pm.PermissionInfo; import android.permission.PermissionManagerInternal; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; -import java.util.function.Consumer; /** * Internal interfaces services. * - * TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes. + * TODO: Move into module. */ -public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal - implements LegacyPermissionDataProvider { +public interface PermissionManagerServiceInternal extends PermissionManagerInternal, + LegacyPermissionDataProvider { /** - * Provider for package names. - */ - public interface PackagesProvider { - - /** - * Gets the packages for a given user. - * @param userId The user id. - * @return The package names. - */ - String[] getPackages(int userId); - } - - /** - * Provider for package names. - */ - public interface SyncAdapterPackagesProvider { - - /** - * Gets the sync adapter packages for given authority and user. - * @param authority The authority. - * @param userId The user id. - * @return The package names. - */ - String[] getPackages(String authority, int userId); - } - - /** - * Provider for default browser - */ - public interface DefaultBrowserProvider { - - /** - * Get the package name of the default browser. - * - * @param userId the user id - * - * @return the package name of the default browser, or {@code null} if none - */ - @Nullable - String getDefaultBrowser(@UserIdInt int userId); - - /** - * Set the package name of the default browser. - * - * @param packageName package name of the default browser, or {@code null} to remove - * @param userId the user id - * - * @return whether the default browser was successfully set. - */ - boolean setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId); - - /** - * Set the package name of the default browser asynchronously. - * - * @param packageName package name of the default browser, or {@code null} to remove - * @param userId the user id - */ - void setDefaultBrowserAsync(@Nullable String packageName, @UserIdInt int userId); - } - - /** - * Provider for default dialer - */ - public interface DefaultDialerProvider { - - /** - * Get the package name of the default dialer. - * - * @param userId the user id - * - * @return the package name of the default dialer, or {@code null} if none - */ - @Nullable - String getDefaultDialer(@UserIdInt int userId); - } - - /** - * Provider for default home + * Adds a listener for runtime permission state (permissions or flags) changes. + * + * @param listener The listener. */ - public interface DefaultHomeProvider { - - /** - * Get the package name of the default home. - * - * @param userId the user id - * - * @return the package name of the default home, or {@code null} if none - */ - @Nullable - String getDefaultHome(@UserIdInt int userId); - - /** - * Set the package name of the default home. - * - * @param packageName package name of the default home, or {@code null} to remove - * @param userId the user id - * @param callback the callback made after the default home as been updated - */ - void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId, - @NonNull Consumer<Boolean> callback); - } + void addOnRuntimePermissionStateChangedListener( + @NonNull OnRuntimePermissionStateChangedListener listener); /** - * Callbacks invoked when interesting actions have been taken on a permission. - * <p> - * NOTE: The current arguments are merely to support the existing use cases. This - * needs to be properly thought out with appropriate arguments for each of the - * callback methods. + * Removes a listener for runtime permission state (permissions or flags) changes. + * + * @param listener The listener. */ - public static class PermissionCallback { - public void onGidsChanged(@AppIdInt int appId, @UserIdInt int userId) { - } - public void onPermissionChanged() { - } - public void onPermissionGranted(int uid, @UserIdInt int userId) { - } - public void onInstallPermissionGranted() { - } - public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) { - } - public void onInstallPermissionRevoked() { - } - public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) { - } - public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync, - int uid) { - onPermissionUpdated(updatedUserIds, sync); - } - public void onPermissionRemoved() { - } - public void onInstallPermissionUpdated() { - } - public void onInstallPermissionUpdatedNotifyListener(int uid) { - onInstallPermissionUpdated(); - } - } + void removeOnRuntimePermissionStateChangedListener( + @NonNull OnRuntimePermissionStateChangedListener listener); - public abstract void systemReady(); + void systemReady(); /** * Get whether permission review is required for a package. @@ -185,26 +64,10 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * @return whether permission review is required */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract boolean isPermissionsReviewRequired(@NonNull String packageName, + boolean isPermissionsReviewRequired(@NonNull String packageName, @UserIdInt int userId); /** - * Update permissions when a package changed. - * - * <p><ol> - * <li>Reconsider the ownership of permission</li> - * <li>Update the state (grant, flags) of the permissions</li> - * </ol> - * - * @param packageName The package that is updated - * @param pkg The package that is updated, or {@code null} if package is deleted - * @param allPackages All currently known packages - * @param callback Callback to call after permission changes - */ - public abstract void updatePermissions(@NonNull String packageName, - @Nullable AndroidPackage pkg); - - /** * Update all permissions for all apps. * * <p><ol> @@ -216,7 +79,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * @param allPackages All currently known packages * @param callback Callback to call after permission changes */ - public abstract void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdate); + void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdate); /** * Reset the runtime permission state changes for a package. @@ -227,7 +90,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * @param userId the user ID */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void resetRuntimePermissions(@NonNull AndroidPackage pkg, + void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId); /** @@ -236,7 +99,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * @param userId the user ID */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void resetAllRuntimePermissions(@UserIdInt int userId); + void resetAllRuntimePermissions(@UserIdInt int userId); /** * Read legacy permission state from package settings. @@ -245,7 +108,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * {@code PackageSetting} which is a implementation detail that permission should not know. * Instead, it should retrieve the legacy state via a defined API. */ - public abstract void readLegacyPermissionStateTEMP(); + void readLegacyPermissionStateTEMP(); /** * Write legacy permission state to package settings. @@ -253,12 +116,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence * for permission. */ - public abstract void writeLegacyPermissionStateTEMP(); - - /** - * Notify that a user has been removed and its permission state should be removed as well. - */ - public abstract void onUserRemoved(@UserIdInt int userId); + void writeLegacyPermissionStateTEMP(); /** * Get all the permissions granted to a package. @@ -269,8 +127,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) @NonNull - public abstract Set<String> getGrantedPermissions(@NonNull String packageName, - @UserIdInt int userId); + Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId); /** * Get the GIDs of a permission. @@ -281,7 +138,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) @NonNull - public abstract int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId); + int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId); /** * Get the packages that have requested an app op permission. @@ -291,172 +148,46 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) @NonNull - public abstract String[] getAppOpPermissionPackages(@NonNull String permissionName); + String[] getAppOpPermissionPackages(@NonNull String permissionName); /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */ @Nullable - public abstract Permission getPermissionTEMP(@NonNull String permName); + Permission getPermissionTEMP(@NonNull String permName); /** Get all permissions that have a certain protection */ - public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionsWithProtection( + @NonNull + ArrayList<PermissionInfo> getAllPermissionsWithProtection( @PermissionInfo.Protection int protection); /** Get all permissions that have certain protection flags */ - public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionsWithProtectionFlags( + @NonNull ArrayList<PermissionInfo> getAllPermissionsWithProtectionFlags( @PermissionInfo.ProtectionFlags int protectionFlags); /** - * Returns the delegate used to influence permission checking. - * - * @return The delegate instance. - */ - public abstract @Nullable CheckPermissionDelegate getCheckPermissionDelegate(); - - /** - * Sets the delegate used to influence permission checking. - * - * @param delegate A delegate instance or {@code null} to clear. - */ - public abstract void setCheckPermissionDelegate(@Nullable CheckPermissionDelegate delegate); - - /** - * Sets the dialer application packages provider. - * @param provider The provider. - */ - public abstract void setDialerAppPackagesProvider(PackagesProvider provider); - - /** - * Set the location extra packages provider. - * @param provider The packages provider. - */ - public abstract void setLocationExtraPackagesProvider(PackagesProvider provider); - - /** - * Sets the location provider packages provider. - * @param provider The packages provider. - */ - public abstract void setLocationPackagesProvider(PackagesProvider provider); - - /** - * Sets the SIM call manager packages provider. - * @param provider The provider. - */ - public abstract void setSimCallManagerPackagesProvider(PackagesProvider provider); - - /** - * Sets the SMS application packages provider. - * @param provider The provider. - */ - public abstract void setSmsAppPackagesProvider(PackagesProvider provider); - - /** - * Sets the sync adapter packages provider. - * @param provider The provider. - */ - public abstract void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider); - - /** - * Sets the Use Open Wifi packages provider. - * @param provider The packages provider. - */ - public abstract void setUseOpenWifiAppPackagesProvider(PackagesProvider provider); - - /** - * Sets the voice interaction packages provider. - * @param provider The packages provider. - */ - public abstract void setVoiceInteractionPackagesProvider(PackagesProvider provider); - - /** - * Sets the default browser provider. - * - * @param provider the provider - */ - public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider); - - /** - * Sets the package name of the default browser provider for the given user. - * - * @param packageName The package name of the default browser or {@code null} - * to clear the default browser - * @param async If {@code true}, set the default browser asynchronously, - * otherwise set it synchronously - * @param doGrant If {@code true} and if {@code packageName} is not {@code null}, - * perform default permission grants on the browser, otherwise skip the - * default permission grants. - * @param userId The user to set the default browser for. - */ - public abstract void setDefaultBrowser(@Nullable String packageName, boolean async, - boolean doGrant, @UserIdInt int userId); - - /** - * Sets the default dialer provider. + * Start delegate the permission identity of the shell UID to the given UID. * - * @param provider the provider + * @param uid the UID to delegate shell permission identity to + * @param packageName the name of the package to delegate shell permission identity to + * @param permissionNames the names of the permissions to delegate shell permission identity + * for, or {@code null} for all permissions */ - public abstract void setDefaultDialerProvider(@NonNull DefaultDialerProvider provider); - - /** - * Sets the default home provider. - * - * @param provider the provider - */ - public abstract void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider); + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + void startShellPermissionIdentityDelegation(int uid, + @NonNull String packageName, @Nullable List<String> permissionNames); /** - * Asynchronously sets the package name of the default home provider for the given user. + * Stop delegating the permission identity of the shell UID. * - * @param packageName The package name of the default home or {@code null} - * to clear the default browser - * @param userId The user to set the default browser for - * @param callback Invoked after the default home has been set + * @see #startShellPermissionIdentityDelegation(int, String, List) */ - public abstract void setDefaultHome(@Nullable String packageName, @UserIdInt int userId, - @NonNull Consumer<Boolean> callback); - - /** - * Returns the default browser package name for the given user. - */ - @Nullable - public abstract String getDefaultBrowser(@UserIdInt int userId); - - /** - * Returns the default dialer package name for the given user. - */ - @Nullable - public abstract String getDefaultDialer(@UserIdInt int userId); - - /** - * Returns the default home package name for the given user. - */ - @Nullable - public abstract String getDefaultHome(@UserIdInt int userId); - - /** - * Requests granting of the default permissions to the current default Use Open Wifi app. - * @param packageName The default use open wifi package name. - * @param userId The user for which to grant the permissions. - */ - public abstract void grantDefaultPermissionsToDefaultSimCallManager( - @NonNull String packageName, @UserIdInt int userId); - - /** - * Requests granting of the default permissions to the current default Use Open Wifi app. - * @param packageName The default use open wifi package name. - * @param userId The user for which to grant the permissions. - */ - public abstract void grantDefaultPermissionsToDefaultUseOpenWifiApp( - @NonNull String packageName, @UserIdInt int userId); - - /** Called when a new user has been created. */ - public abstract void onNewUserCreated(@UserIdInt int userId); + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + void stopShellPermissionIdentityDelegation(); /** * Removes invalid permissions which are not {@link PermissionInfo#FLAG_HARD_RESTRICTED} or * {@link PermissionInfo#FLAG_SOFT_RESTRICTED} from the input. */ - public abstract void retainHardAndSoftRestrictedPermissions( - @NonNull List<String> permissionNames); + void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissionNames); /** * Read legacy permissions from legacy permission settings. @@ -465,8 +196,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * {@code LegacyPermissionSettings} which is a implementation detail that permission should not * know. Instead, it should retrieve the legacy permissions via a defined API. */ - public abstract void readLegacyPermissionsTEMP( - @NonNull LegacyPermissionSettings legacyPermissionSettings); + void readLegacyPermissionsTEMP(@NonNull LegacyPermissionSettings legacyPermissionSettings); /** * Write legacy permissions to legacy permission settings. @@ -474,8 +204,23 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence * for permission. */ - public abstract void writeLegacyPermissionsTEMP( - @NonNull LegacyPermissionSettings legacyPermissionSettings); + void writeLegacyPermissionsTEMP(@NonNull LegacyPermissionSettings legacyPermissionSettings); + + /** + * Callback when a user has been created. + * + * @param userId the created user ID + */ + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + void onUserCreated(@UserIdInt int userId); + + /** + * Callback when a user has been removed. + * + * @param userId the removed user ID + */ + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + void onUserRemoved(@UserIdInt int userId); /** * Callback when a package has been added. @@ -485,23 +230,19 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * @param oldPkg the old package, or {@code null} if none */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp, + void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp, @Nullable AndroidPackage oldPkg); /** - * Callback when a package has been installed for certain users. + * Callback when a package has been installed for a user. * * @param pkg the installed package - * @param grantedPermissions the permissions to be granted - * @param allowlistedRestrictedPermissions the restricted permissions to be allowlisted - * @param autoRevokePermissionsMode the auto revoke permissions mode for this package + * @param params the parameters passed in for package installation * @param userId the user ID this package is installed for */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void onPackageInstalled(@NonNull AndroidPackage pkg, - @NonNull List<String> grantedPermissions, - @NonNull List<String> allowlistedRestrictedPermissions, - int autoRevokePermissionsMode, @UserIdInt int userId); + void onPackageInstalled(@NonNull AndroidPackage pkg, @NonNull PackageInstalledParams params, + @UserIdInt int userId); /** * Callback when a package has been removed. @@ -509,19 +250,25 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * @param pkg the removed package */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void onPackageRemoved(@NonNull AndroidPackage pkg); + void onPackageRemoved(@NonNull AndroidPackage pkg); /** - * Callback when the state for a package has been removed. + * Callback when a package has been uninstalled. + * <p> + * The package may have been fully removed from the system, or only marked as uninstalled for + * this user but still instlaled for other users. + * + * TODO: Pass PackageState instead. * - * @param packageName the name of the removed package - * @param appId the app ID of the removed package - * @param pkg the removed package, or {@code null} if unavailable + * @param packageName the name of the uninstalled package + * @param appId the app ID of the uninstalled package + * @param pkg the uninstalled package, or {@code null} if unavailable * @param sharedUserPkgs the packages that are in the same shared user + * @param userId the user ID the package is uninstalled for */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void onPackageStateRemoved(@NonNull String packageName, int appId, - @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs); + void onPackageUninstalled(@NonNull String packageName, int appId, @Nullable AndroidPackage pkg, + @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId); /** * Check whether a permission can be propagated to instant app. @@ -529,5 +276,149 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * @param permissionName the name of the permission * @return whether the permission can be propagated */ - public abstract boolean canPropagatePermissionToInstantApp(@NonNull String permissionName); + boolean canPropagatePermissionToInstantApp(@NonNull String permissionName); + + /** + * Listener for package permission state (permissions or flags) changes. + */ + interface OnRuntimePermissionStateChangedListener { + + /** + * Called when the runtime permission state (permissions or flags) changed. + * + * @param packageName The package for which the change happened. + * @param userId the user id for which the change happened. + */ + @Nullable + void onRuntimePermissionStateChanged(@NonNull String packageName, + @UserIdInt int userId); + } + + /** + * The permission-related parameters passed in for package installation. + * + * @see android.content.pm.PackageInstaller.SessionParams + */ + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + final class PackageInstalledParams { + /** + * A static instance whose parameters are all in their default state. + */ + public static final PackageInstalledParams DEFAULT = new Builder().build(); + + @NonNull + private final List<String> mGrantedPermissions; + @NonNull + private final List<String> mAllowlistedRestrictedPermissions; + @NonNull + private final int mAutoRevokePermissionsMode; + + private PackageInstalledParams(@NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, + int autoRevokePermissionsMode) { + mGrantedPermissions = grantedPermissions; + mAllowlistedRestrictedPermissions = allowlistedRestrictedPermissions; + mAutoRevokePermissionsMode = autoRevokePermissionsMode; + } + + /** + * Get the permissions to be granted. + * + * @return the permissions to be granted + */ + @NonNull + public List<String> getGrantedPermissions() { + return mGrantedPermissions; + } + + /** + * Get the restricted permissions to be allowlisted. + * + * @return the restricted permissions to be allowlisted + */ + @NonNull + public List<String> getAllowlistedRestrictedPermissions() { + return mAllowlistedRestrictedPermissions; + } + + /** + * Get the mode for auto revoking permissions. + * + * @return the mode for auto revoking permissions + */ + public int getAutoRevokePermissionsMode() { + return mAutoRevokePermissionsMode; + } + + /** + * Builder class for {@link PackageInstalledParams}. + */ + public static final class Builder { + @NonNull + private List<String> mGrantedPermissions = Collections.emptyList(); + @NonNull + private List<String> mAllowlistedRestrictedPermissions = Collections.emptyList(); + @NonNull + private int mAutoRevokePermissionsMode = AppOpsManager.MODE_DEFAULT; + + /** + * Set the permissions to be granted. + * + * @param grantedPermissions the permissions to be granted + * + * @see android.content.pm.PackageInstaller.SessionParams#setGrantedRuntimePermissions( + * java.lang.String[]) + */ + public void setGrantedPermissions(@NonNull List<String> grantedPermissions) { + Objects.requireNonNull(grantedPermissions); + mGrantedPermissions = new ArrayList<>(grantedPermissions); + } + + /** + * Set the restricted permissions to be allowlisted. + * <p> + * Permissions that are not restricted are ignored, so one can just pass in all + * requested permissions of a package to get all its restricted permissions allowlisted. + * + * @param allowlistedRestrictedPermissions the restricted permissions to be allowlisted + * + * @see android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set) + */ + public void setAllowlistedRestrictedPermissions( + @NonNull List<String> allowlistedRestrictedPermissions) { + Objects.requireNonNull(mGrantedPermissions); + mAllowlistedRestrictedPermissions = new ArrayList<>( + allowlistedRestrictedPermissions); + } + + /** + * Set the mode for auto revoking permissions. + * <p> + * {@link AppOpsManager#MODE_ALLOWED} means the system is allowed to auto revoke + * permissions from this package, and {@link AppOpsManager#MODE_IGNORED} means this + * package should be ignored when auto revoking permissions. + * {@link AppOpsManager#MODE_DEFAULT} means no changes will be made to the auto revoke + * mode of this package. + * + * @param autoRevokePermissionsMode the mode for auto revoking permissions + * + * @see android.content.pm.PackageInstaller.SessionParams#setAutoRevokePermissionsMode( + * boolean) + */ + public void setAutoRevokePermissionsMode(int autoRevokePermissionsMode) { + mAutoRevokePermissionsMode = autoRevokePermissionsMode; + } + + /** + * Build a new instance of {@link PackageInstalledParams}. + * + * @return the {@link PackageInstalledParams} built + */ + @NonNull + public PackageInstalledParams build() { + return new PackageInstalledParams(mGrantedPermissions, + mAllowlistedRestrictedPermissions, mAutoRevokePermissionsMode); + } + } + } } diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java index 321bb8c0251d..cc1f8d620d1f 100644 --- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java +++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java @@ -158,7 +158,6 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, // switch there is no need to register for a callback. boolean shouldListenToLidSwitch = false; - final SensorManager sensorManager = mContext.getSystemService(SensorManager.class); // The set of Sensor(s) that this instance should register to receive SensorEvent(s) from. final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>(); @@ -182,19 +181,10 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, List<SensorCondition> sensorConditions = conditions.getSensor(); for (int j = 0; j < sensorConditions.size(); j++) { SensorCondition sensorCondition = sensorConditions.get(j); - final int expectedSensorType = sensorCondition.getType().intValue(); + final String expectedSensorType = sensorCondition.getType(); final String expectedSensorName = sensorCondition.getName(); - List<Sensor> sensors = sensorManager.getSensorList(expectedSensorType); - Sensor foundSensor = null; - for (int sensorIndex = 0; sensorIndex < sensors.size(); sensorIndex++) { - Sensor sensor = sensors.get(sensorIndex); - if (sensor.getName().equals(expectedSensorName)) { - foundSensor = sensor; - break; - } - } - + final Sensor foundSensor = findSensor(expectedSensorType, expectedSensorName); if (foundSensor == null) { throw new IllegalStateException("Failed to find Sensor with type: " + expectedSensorType + " and name: " + expectedSensorName); @@ -221,12 +211,33 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, inputManager.registerLidSwitchCallback(this); } + final SensorManager sensorManager = mContext.getSystemService(SensorManager.class); for (int i = 0; i < sensorsToListenTo.size(); i++) { Sensor sensor = sensorsToListenTo.valueAt(i); sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST); } } + @Nullable + private Sensor findSensor(String type, String name) { + final SensorManager sensorManager = mContext.getSystemService(SensorManager.class); + final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL); + for (int sensorIndex = 0; sensorIndex < sensors.size(); sensorIndex++) { + final Sensor sensor = sensors.get(sensorIndex); + final String sensorType = sensor.getStringType(); + final String sensorName = sensor.getName(); + + if (sensorType == null || sensorName == null) { + continue; + } + + if (sensorType.equals(type) && sensorName.equals(name)) { + return sensor; + } + } + return null; + } + @Override public void setListener(Listener listener) { synchronized (mLock) { diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java new file mode 100644 index 000000000000..84ac12497e71 --- /dev/null +++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java @@ -0,0 +1,229 @@ +/* + * 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.policy; + +import static android.view.KeyEvent.KEYCODE_POWER; + +import android.os.SystemClock; +import android.util.SparseLongArray; +import android.view.KeyEvent; + +import com.android.internal.util.ToBooleanFunction; + +import java.util.ArrayList; +import java.util.function.Consumer; + +/** + * Handles a mapping of two keys combination. + */ +public class KeyCombinationManager { + private static final String TAG = "KeyCombinationManager"; + + // Store the received down time of keycode. + private final SparseLongArray mDownTimes = new SparseLongArray(2); + private final ArrayList<TwoKeysCombinationRule> mRules = new ArrayList(); + + // Selected rules according to current key down. + private final ArrayList<TwoKeysCombinationRule> mActiveRules = new ArrayList(); + // The rule has been triggered by current keys. + private TwoKeysCombinationRule mTriggeredRule; + + // Keys in a key combination must be pressed within this interval of each other. + private static final long COMBINE_KEY_DELAY_MILLIS = 150; + + /** + * Rule definition for two keys combination. + * E.g : define volume_down + power key. + * <pre class="prettyprint"> + * TwoKeysCombinationRule rule = + * new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) { + * boolean preCondition() { // check if it needs to intercept key } + * void execute() { // trigger action } + * void cancel() { // cancel action } + * }; + * </pre> + */ + abstract static class TwoKeysCombinationRule { + private int mKeyCode1; + private int mKeyCode2; + + TwoKeysCombinationRule(int keyCode1, int keyCode2) { + mKeyCode1 = keyCode1; + mKeyCode2 = keyCode2; + } + + boolean preCondition() { + return true; + } + + boolean shouldInterceptKey(int keyCode) { + return preCondition() && (keyCode == mKeyCode1 || keyCode == mKeyCode2); + } + + boolean shouldInterceptKeys(SparseLongArray downTimes) { + final long now = SystemClock.uptimeMillis(); + if (downTimes.get(mKeyCode1) > 0 + && downTimes.get(mKeyCode2) > 0 + && now <= downTimes.get(mKeyCode1) + COMBINE_KEY_DELAY_MILLIS + && now <= downTimes.get(mKeyCode2) + COMBINE_KEY_DELAY_MILLIS) { + return true; + } + return false; + } + + abstract void execute(); + abstract void cancel(); + + @Override + public String toString() { + return "KeyCode1 = " + KeyEvent.keyCodeToString(mKeyCode1) + + ", KeyCode2 = " + KeyEvent.keyCodeToString(mKeyCode2); + } + } + + public KeyCombinationManager() { + } + + void addRule(TwoKeysCombinationRule rule) { + mRules.add(rule); + } + + /** + * Check if the key event could be triggered by combine key rule before dispatching to a window. + */ + void interceptKey(KeyEvent event, boolean interactive) { + final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; + final int keyCode = event.getKeyCode(); + final int count = mActiveRules.size(); + final long eventTime = event.getEventTime(); + + if (interactive && down) { + if (mDownTimes.size() > 0) { + if (count > 0 + && eventTime > mDownTimes.valueAt(0) + COMBINE_KEY_DELAY_MILLIS) { + // exceed time from first key down. + forAllRules(mActiveRules, (rule)-> rule.cancel()); + mActiveRules.clear(); + return; + } else if (count == 0) { // has some key down but no active rule exist. + return; + } + } + + if (mDownTimes.get(keyCode) == 0) { + mDownTimes.put(keyCode, eventTime); + } else { + // ignore old key, maybe a repeat key. + return; + } + + if (mDownTimes.size() == 1) { + mTriggeredRule = null; + // check first key and pick active rules. + forAllRules(mRules, (rule)-> { + if (rule.shouldInterceptKey(keyCode)) { + mActiveRules.add(rule); + } + }); + } else { + // Ignore if rule already triggered. + if (mTriggeredRule != null) { + return; + } + + // check if second key can trigger rule, or remove the non-match rule. + forAllActiveRules((rule) -> { + if (!rule.shouldInterceptKeys(mDownTimes)) { + return false; + } + rule.execute(); + mTriggeredRule = rule; + return true; + }); + mActiveRules.clear(); + if (mTriggeredRule != null) { + mActiveRules.add(mTriggeredRule); + } + } + } else { + mDownTimes.delete(keyCode); + for (int index = count - 1; index >= 0; index--) { + final TwoKeysCombinationRule rule = mActiveRules.get(index); + if (rule.shouldInterceptKey(keyCode)) { + rule.cancel(); + mActiveRules.remove(index); + } + } + } + } + + /** + * Return the interceptTimeout to tell InputDispatcher when is ready to deliver to window. + */ + long getKeyInterceptTimeout(int keyCode) { + if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) { + return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS; + } + return 0; + } + + /** + * True if the key event had been handled. + */ + boolean isKeyConsumed(KeyEvent event) { + if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) { + return false; + } + return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode()); + } + + /** + * True if power key is the candidate. + */ + boolean isPowerKeyIntercepted() { + if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) { + // return false if only if power key pressed. + return mDownTimes.size() > 1 || mDownTimes.get(KEYCODE_POWER) == 0; + } + return false; + } + + /** + * Traverse each item of rules. + */ + private void forAllRules( + ArrayList<TwoKeysCombinationRule> rules, Consumer<TwoKeysCombinationRule> callback) { + final int count = rules.size(); + for (int index = 0; index < count; index++) { + final TwoKeysCombinationRule rule = rules.get(index); + callback.accept(rule); + } + } + + /** + * Traverse each item of active rules until some rule can be applied, otherwise return false. + */ + private boolean forAllActiveRules(ToBooleanFunction<TwoKeysCombinationRule> callback) { + final int count = mActiveRules.size(); + for (int index = 0; index < count; index++) { + final TwoKeysCombinationRule rule = mActiveRules.get(index); + if (callback.apply(rule)) { + return true; + } + } + return false; + } +} diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 8beec35ebc64..75868e3216cc 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -35,7 +35,13 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.STATE_OFF; +import static android.view.KeyEvent.KEYCODE_BACK; +import static android.view.KeyEvent.KEYCODE_DPAD_CENTER; +import static android.view.KeyEvent.KEYCODE_DPAD_DOWN; +import static android.view.KeyEvent.KEYCODE_POWER; import static android.view.KeyEvent.KEYCODE_UNKNOWN; +import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN; +import static android.view.KeyEvent.KEYCODE_VOLUME_UP; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; @@ -202,6 +208,7 @@ import com.android.server.GestureLauncherService; import com.android.server.LocalServices; import com.android.server.SystemServiceManager; import com.android.server.inputmethod.InputMethodManagerInternal; +import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule; import com.android.server.policy.keyguard.KeyguardServiceDelegate; import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener; import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback; @@ -405,7 +412,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mEnableCarDockHomeCapture = true; boolean mBootMessageNeedsHiding; - KeyguardServiceDelegate mKeyguardDelegate; + private KeyguardServiceDelegate mKeyguardDelegate; private boolean mKeyguardBound; final Runnable mWindowManagerDrawCallback = new Runnable() { @Override @@ -422,8 +429,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; - GlobalActions mGlobalActions; - Handler mHandler; + private GlobalActions mGlobalActions; + private Handler mHandler; // FIXME This state is shared between the input reader and handler thread. // Technically it's broken and buggy but it has been like this for many years @@ -547,34 +554,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mGoToSleepOnButtonPressTheaterMode; // Screenshot trigger states - // Time to volume and power must be pressed within this interval of each other. - private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150; // Increase the chord delay when taking a screenshot from the keyguard private static final float KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER = 2.5f; - private boolean mScreenshotChordEnabled; - private boolean mScreenshotChordVolumeDownKeyTriggered; - private long mScreenshotChordVolumeDownKeyTime; - private boolean mScreenshotChordVolumeDownKeyConsumed; - private boolean mA11yShortcutChordVolumeUpKeyTriggered; - private long mA11yShortcutChordVolumeUpKeyTime; - private boolean mA11yShortcutChordVolumeUpKeyConsumed; - - private boolean mScreenshotChordPowerKeyTriggered; - private long mScreenshotChordPowerKeyTime; // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up - private int mRingerToggleChord = VOLUME_HUSH_OFF; + int mRingerToggleChord = VOLUME_HUSH_OFF; private static final long BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS = 1000; - private boolean mBugreportTvKey1Pressed; - private boolean mBugreportTvKey2Pressed; - private boolean mBugreportTvScheduled; - - private boolean mAccessibilityTvKey1Pressed; - private boolean mAccessibilityTvKey2Pressed; - private boolean mAccessibilityTvScheduled; - /* The number of steps between min and max brightness */ private static final int BRIGHTNESS_STEPS = 10; @@ -603,6 +590,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS; + private KeyCombinationManager mKeyCombinationManager; + private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4; private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5; @@ -900,15 +889,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWindowManagerFuncs.onPowerKeyDown(interactive); - // Latch power key state to detect screenshot chord. - if (interactive && !mScreenshotChordPowerKeyTriggered - && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { - mScreenshotChordPowerKeyTriggered = true; - mScreenshotChordPowerKeyTime = event.getDownTime(); - interceptScreenshotChord(); - interceptRingerToggleChord(); - } - // Stop ringing or end call if configured to do so when power is pressed. TelecomManager telecomManager = getTelecommService(); boolean hungUp = false; @@ -946,9 +926,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // If the power key has still not yet been handled, then detect short // press, long press, or multi press and decide what to do. - mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered - || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted - || handledByPowerManager; + mPowerKeyHandled = hungUp || gesturedServiceIntercepted + || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted(); if (!mPowerKeyHandled) { if (interactive) { // When interactive, we're already awake. @@ -1004,8 +983,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) { final boolean handled = canceled || mPowerKeyHandled; - mScreenshotChordPowerKeyTriggered = false; - cancelPendingScreenshotChordAction(); cancelPendingPowerKeyAction(); if (!handled) { @@ -1315,52 +1292,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void interceptScreenshotChord() { - if (mScreenshotChordEnabled - && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered - && !mA11yShortcutChordVolumeUpKeyTriggered) { - final long now = SystemClock.uptimeMillis(); - if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS - && now <= mScreenshotChordPowerKeyTime - + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) { - mScreenshotChordVolumeDownKeyConsumed = true; - cancelPendingPowerKeyAction(); - mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); - mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD); - mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay()); - } - } + mHandler.removeCallbacks(mScreenshotRunnable); + mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); + mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD); + mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay()); } private void interceptAccessibilityShortcutChord() { - if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(isKeyguardLocked()) - && mScreenshotChordVolumeDownKeyTriggered && mA11yShortcutChordVolumeUpKeyTriggered - && !mScreenshotChordPowerKeyTriggered) { - final long now = SystemClock.uptimeMillis(); - if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS - && now <= mA11yShortcutChordVolumeUpKeyTime - + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) { - mScreenshotChordVolumeDownKeyConsumed = true; - mA11yShortcutChordVolumeUpKeyConsumed = true; - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT), - getAccessibilityShortcutTimeout()); - } - } + mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT), + getAccessibilityShortcutTimeout()); } private void interceptRingerToggleChord() { - if (mRingerToggleChord != Settings.Secure.VOLUME_HUSH_OFF - && mScreenshotChordPowerKeyTriggered && mA11yShortcutChordVolumeUpKeyTriggered) { - final long now = SystemClock.uptimeMillis(); - if (now <= mA11yShortcutChordVolumeUpKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS - && now <= mScreenshotChordPowerKeyTime - + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) { - mA11yShortcutChordVolumeUpKeyConsumed = true; - cancelPendingPowerKeyAction(); - - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD), - getRingerToggleChordDelay()); - } - } + mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD), + getRingerToggleChordDelay()); } private long getAccessibilityShortcutTimeout() { @@ -1942,9 +1889,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(), com.android.internal.R.array.config_safeModeEnabledVibePattern); - mScreenshotChordEnabled = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_enableScreenshotChord); - mGlobalKeyManager = new GlobalKeyManager(mContext); // Controls rotation and the like. @@ -1980,6 +1924,92 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged(); } }); + initKeyCombinationRules(); + } + + private void initKeyCombinationRules() { + mKeyCombinationManager = new KeyCombinationManager(); + final boolean screenshotChordEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enableScreenshotChord); + + if (screenshotChordEnabled) { + mKeyCombinationManager.addRule( + new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) { + @Override + void execute() { + cancelPendingPowerKeyAction(); + interceptScreenshotChord(); + } + @Override + void cancel() { + cancelPendingScreenshotChordAction(); + } + }); + } + + mKeyCombinationManager.addRule( + new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP) { + @Override + boolean preCondition() { + return mAccessibilityShortcutController + .isAccessibilityShortcutAvailable(isKeyguardLocked()); + } + @Override + void execute() { + interceptAccessibilityShortcutChord(); + } + @Override + void cancel() { + cancelPendingAccessibilityShortcutAction(); + } + }); + + mKeyCombinationManager.addRule( + new TwoKeysCombinationRule(KEYCODE_VOLUME_UP, KEYCODE_POWER) { + @Override + boolean preCondition() { + return mRingerToggleChord != VOLUME_HUSH_OFF; + } + @Override + void execute() { + cancelPendingPowerKeyAction(); + interceptRingerToggleChord(); + } + @Override + void cancel() { + cancelPendingRingerToggleChordAction(); + } + }); + + if (mHasFeatureLeanback) { + mKeyCombinationManager.addRule( + new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) { + @Override + void execute() { + cancelPendingBackKeyAction(); + interceptAccessibilityGestureTv(); + } + + @Override + void cancel() { + cancelAccessibilityGestureTv(); + } + }); + + mKeyCombinationManager.addRule( + new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) { + @Override + void execute() { + cancelPendingBackKeyAction(); + interceptBugreportGestureTv(); + } + + @Override + void cancel() { + cancelBugreportGestureTv(); + } + }); + } } /** @@ -2552,70 +2582,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { + repeatCount + " keyguardOn=" + keyguardOn + " canceled=" + canceled); } - // If we think we might have a volume down & power key chord on the way - // but we're not sure, then tell the dispatcher to wait a little while and - // try again later before dispatching. - if (mScreenshotChordEnabled && (flags & KeyEvent.FLAG_FALLBACK) == 0) { - if (mScreenshotChordVolumeDownKeyTriggered && !mScreenshotChordPowerKeyTriggered) { - final long now = SystemClock.uptimeMillis(); - final long timeoutTime = mScreenshotChordVolumeDownKeyTime - + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS; - if (now < timeoutTime) { - return timeoutTime - now; - } - } - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - && mScreenshotChordVolumeDownKeyConsumed) { - if (!down) { - mScreenshotChordVolumeDownKeyConsumed = false; - } - return -1; - } - } - - // If an accessibility shortcut might be partially complete, hold off dispatching until we - // know if it is complete or not - if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false) - && (flags & KeyEvent.FLAG_FALLBACK) == 0) { - if (mScreenshotChordVolumeDownKeyTriggered ^ mA11yShortcutChordVolumeUpKeyTriggered) { - final long now = SystemClock.uptimeMillis(); - final long timeoutTime = (mScreenshotChordVolumeDownKeyTriggered - ? mScreenshotChordVolumeDownKeyTime : mA11yShortcutChordVolumeUpKeyTime) - + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS; - if (now < timeoutTime) { - return timeoutTime - now; - } - } - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mScreenshotChordVolumeDownKeyConsumed) { - if (!down) { - mScreenshotChordVolumeDownKeyConsumed = false; - } - return -1; - } - if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mA11yShortcutChordVolumeUpKeyConsumed) { - if (!down) { - mA11yShortcutChordVolumeUpKeyConsumed = false; - } - return -1; - } + if (mKeyCombinationManager.isKeyConsumed(event)) { + return -1; } - // If a ringer toggle chord could be on the way but we're not sure, then tell the dispatcher - // to wait a little while and try again later before dispatching. - if (mRingerToggleChord != VOLUME_HUSH_OFF && (flags & KeyEvent.FLAG_FALLBACK) == 0) { - if (mA11yShortcutChordVolumeUpKeyTriggered && !mScreenshotChordPowerKeyTriggered) { - final long now = SystemClock.uptimeMillis(); - final long timeoutTime = mA11yShortcutChordVolumeUpKeyTime - + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS; - if (now < timeoutTime) { - return timeoutTime - now; - } - } - if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mA11yShortcutChordVolumeUpKeyConsumed) { - if (!down) { - mA11yShortcutChordVolumeUpKeyConsumed = false; - } - return -1; + if ((flags & KeyEvent.FLAG_FALLBACK) == 0) { + final long now = SystemClock.uptimeMillis(); + final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(keyCode); + if (now < interceptTimeout) { + return interceptTimeout - now; } } @@ -2774,8 +2749,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) { // Pass through keyboard navigation keys. return 0; - } else if (mHasFeatureLeanback && interceptBugreportGestureTv(keyCode, down)) { - return -1; } else if (keyCode == KeyEvent.KEYCODE_ALL_APPS) { if (!down) { mHandler.removeMessages(MSG_HANDLE_ALL_APPS); @@ -2978,53 +2951,30 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** * TV only: recognizes a remote control gesture for capturing a bug report. */ - private boolean interceptBugreportGestureTv(int keyCode, boolean down) { + private void interceptBugreportGestureTv() { + mHandler.removeMessages(MSG_BUGREPORT_TV); // The bugreport capture chord is a long press on DPAD CENTER and BACK simultaneously. - if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { - mBugreportTvKey1Pressed = down; - } else if (keyCode == KeyEvent.KEYCODE_BACK) { - mBugreportTvKey2Pressed = down; - } - - if (mBugreportTvKey1Pressed && mBugreportTvKey2Pressed) { - if (!mBugreportTvScheduled) { - mBugreportTvScheduled = true; - Message msg = Message.obtain(mHandler, MSG_BUGREPORT_TV); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS); - } - } else if (mBugreportTvScheduled) { - mHandler.removeMessages(MSG_BUGREPORT_TV); - mBugreportTvScheduled = false; - } + Message msg = Message.obtain(mHandler, MSG_BUGREPORT_TV); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS); + } - return mBugreportTvScheduled; + private void cancelBugreportGestureTv() { + mHandler.removeMessages(MSG_BUGREPORT_TV); } /** * TV only: recognizes a remote control gesture as Accessibility shortcut. * Shortcut: Long press (BACK + DPAD_DOWN) */ - private boolean interceptAccessibilityGestureTv(int keyCode, boolean down) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - mAccessibilityTvKey1Pressed = down; - } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { - mAccessibilityTvKey2Pressed = down; - } - - if (mAccessibilityTvKey1Pressed && mAccessibilityTvKey2Pressed) { - if (!mAccessibilityTvScheduled) { - mAccessibilityTvScheduled = true; - Message msg = Message.obtain(mHandler, MSG_ACCESSIBILITY_TV); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, getAccessibilityShortcutTimeout()); - } - } else if (mAccessibilityTvScheduled) { - mHandler.removeMessages(MSG_ACCESSIBILITY_TV); - mAccessibilityTvScheduled = false; - } - - return mAccessibilityTvScheduled; + private void interceptAccessibilityGestureTv() { + mHandler.removeMessages(MSG_ACCESSIBILITY_TV); + Message msg = Message.obtain(mHandler, MSG_ACCESSIBILITY_TV); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, getAccessibilityShortcutTimeout()); + } + private void cancelAccessibilityGestureTv() { + mHandler.removeMessages(MSG_ACCESSIBILITY_TV); } private void requestBugreportForTv() { @@ -3547,16 +3497,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int displayId = event.getDisplayId(); final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0; - // If screen is off then we treat the case where the keyguard is open but hidden - // the same as if it were open and in front. - // This will prevent any keys other than the power button from waking the screen - // when the keyguard is hidden by another activity. - final boolean keyguardActive = (mKeyguardDelegate == null ? false : - (interactive ? - isKeyguardShowingAndNotOccluded() : - mKeyguardDelegate.isShowing())); - if (DEBUG_INPUT) { + // If screen is off then we treat the case where the keyguard is open but hidden + // the same as if it were open and in front. + // This will prevent any keys other than the power button from waking the screen + // when the keyguard is hidden by another activity. + final boolean keyguardActive = (mKeyguardDelegate != null + && (interactive ? isKeyguardShowingAndNotOccluded() : + mKeyguardDelegate.isShowing())); Log.d(TAG, "interceptKeyTq keycode=" + keyCode + " interactive=" + interactive + " keyguardActive=" + keyguardActive + " policyFlags=" + Integer.toHexString(policyFlags)); @@ -3581,7 +3529,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Reset the pending key mPendingWakeKey = PENDING_KEY_NULL; } - } else if (!interactive && shouldDispatchInputWhenNonInteractive(displayId, keyCode)) { + } else if (shouldDispatchInputWhenNonInteractive(displayId, keyCode)) { // If we're currently dozing with the screen on and the keyguard showing, pass the key // to the application but preserve its wake key status to make sure we still move // from dozing to fully interactive if we would normally go from off to fully @@ -3613,6 +3561,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { return result; } + if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { + mKeyCombinationManager.interceptKey(event, interactive); + } + // Enable haptics if down and virtual key without multiple repetitions. If this is a hard // virtual key such as a navigation bar button, only vibrate if flag is enabled. final boolean isNavBarVirtKey = ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0); @@ -3640,46 +3592,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_MUTE: { - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { - if (down) { - // Any activity on the vol down button stops the ringer toggle shortcut - cancelPendingRingerToggleChordAction(); - - if (interactive && !mScreenshotChordVolumeDownKeyTriggered - && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { - mScreenshotChordVolumeDownKeyTriggered = true; - mScreenshotChordVolumeDownKeyTime = event.getDownTime(); - mScreenshotChordVolumeDownKeyConsumed = false; - cancelPendingPowerKeyAction(); - interceptScreenshotChord(); - interceptAccessibilityShortcutChord(); - } - } else { - mScreenshotChordVolumeDownKeyTriggered = false; - cancelPendingScreenshotChordAction(); - cancelPendingAccessibilityShortcutAction(); - } - } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { - if (down) { - if (interactive && !mA11yShortcutChordVolumeUpKeyTriggered - && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { - mA11yShortcutChordVolumeUpKeyTriggered = true; - mA11yShortcutChordVolumeUpKeyTime = event.getDownTime(); - mA11yShortcutChordVolumeUpKeyConsumed = false; - cancelPendingPowerKeyAction(); - cancelPendingScreenshotChordAction(); - cancelPendingRingerToggleChordAction(); - - interceptAccessibilityShortcutChord(); - interceptRingerToggleChord(); - } - } else { - mA11yShortcutChordVolumeUpKeyTriggered = false; - cancelPendingScreenshotChordAction(); - cancelPendingAccessibilityShortcutAction(); - cancelPendingRingerToggleChordAction(); - } - } if (down) { sendSystemKeyToStatusBarAsync(event.getKeyCode()); @@ -3784,7 +3696,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { KeyEvent.actionToString(event.getAction()), mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter); // Any activity on the power button stops the accessibility shortcut - cancelPendingAccessibilityShortcutAction(); result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately if (down) { @@ -3922,22 +3833,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - // Intercept the Accessibility keychord for TV (DPAD_DOWN + Back) before the keyevent is - // processed through interceptKeyEventBeforeDispatch since Talkback may consume this event - // before it has a chance to reach that method. - if (mHasFeatureLeanback) { - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_BACK: { - boolean handled = interceptAccessibilityGestureTv(keyCode, down); - if (handled) { - result &= ~ACTION_PASS_TO_USER; - } - break; - } - } - } - // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users. if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(isKeyguardLocked())) { switch (keyCode) { @@ -5388,14 +5283,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(!mAllowLockscreenWhenOnDisplays.isEmpty()); pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout); pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive); - if (mHasFeatureLeanback) { - pw.print(prefix); - pw.print("mAccessibilityTvKey1Pressed="); pw.println(mAccessibilityTvKey1Pressed); - pw.print(prefix); - pw.print("mAccessibilityTvKey2Pressed="); pw.println(mAccessibilityTvKey2Pressed); - pw.print(prefix); - pw.print("mAccessibilityTvScheduled="); pw.println(mAccessibilityTvScheduled); - } mGlobalKeyManager.dump(prefix, pw); diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index 1e4e0a6a04bc..68d038bb5cf6 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -368,12 +368,6 @@ public class BatterySaverController implements BatterySaverPolicyListener { } } - boolean setAdaptivePolicyLocked(String settings, String deviceSpecificSettings, int reason) { - return setAdaptivePolicyLocked( - BatterySaverPolicy.Policy.fromSettings(settings, deviceSpecificSettings), - reason); - } - boolean setAdaptivePolicyLocked(BatterySaverPolicyConfig config, int reason) { return setAdaptivePolicyLocked(BatterySaverPolicy.Policy.fromConfig(config), reason); } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java index 1883f4e4e86c..8eb66cdee5cd 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java @@ -26,9 +26,11 @@ import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerSaveState; +import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.IndentingPrintWriter; import android.util.KeyValueListParser; import android.util.Slog; import android.view.accessibility.AccessibilityManager; @@ -47,6 +49,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; /** * Class to decide whether to turn on battery saver mode for specific services. @@ -56,60 +59,81 @@ import java.util.Objects; * * Test: atest com.android.server.power.batterysaver.BatterySaverPolicyTest */ -public class BatterySaverPolicy extends ContentObserver { +public class BatterySaverPolicy extends ContentObserver implements + DeviceConfig.OnPropertiesChangedListener { private static final String TAG = "BatterySaverPolicy"; static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE. - private static final String KEY_GPS_MODE = "gps_mode"; - private static final String KEY_VIBRATION_DISABLED = "vibration_disabled"; - private static final String KEY_ANIMATION_DISABLED = "animation_disabled"; - private static final String KEY_SOUNDTRIGGER_DISABLED = "soundtrigger_disabled"; + @VisibleForTesting + static final String KEY_LOCATION_MODE = "location_mode"; + @VisibleForTesting + static final String KEY_DISABLE_VIBRATION = "disable_vibration"; + @VisibleForTesting + static final String KEY_DISABLE_ANIMATION = "disable_animation"; + @VisibleForTesting + static final String KEY_DISABLE_SOUNDTRIGGER = "disable_soundtrigger"; /** - * Disable turning on the network firewall when Battery Saver is turned on. - * If set to false, the firewall WILL be turned on when Battery Saver is turned on. - * If set to true, the firewall WILL NOT be turned on when Battery Saver is turned on. + * Turn on the network firewall when Battery Saver is turned on. + * If set to false, the firewall WILL NOT be turned on when Battery Saver is turned on. + * If set to true, the firewall WILL be turned on when Battery Saver is turned on. */ - private static final String KEY_ACTIVATE_FIREWALL_DISABLED = "firewall_disabled"; + @VisibleForTesting + static final String KEY_ENABLE_FIREWALL = "enable_firewall"; /** - * Disable turning on the special low power screen brightness dimming when Battery Saver is + * Turn on the special low power screen brightness dimming when Battery Saver is * turned on. - * If set to false, the screen brightness dimming WILL be turned on by Battery Saver. - * If set to true, the screen brightness WILL NOT be turned on by Battery Saver. + * If set to false, the screen brightness dimming WILL NOT be turned on by Battery Saver. + * If set to true, the screen brightness WILL be turned on by Battery Saver. */ - private static final String KEY_ADJUST_BRIGHTNESS_DISABLED = "adjust_brightness_disabled"; + @VisibleForTesting + static final String KEY_ENABLE_BRIGHTNESS_ADJUSTMENT = "enable_brightness_adjustment"; /** - * Disable turning on Data Saver when Battery Saver is turned on. - * If set to false, Data Saver WILL be turned on when Battery Saver is turned on. - * If set to true, Data Saver WILL NOT be turned on when Battery Saver is turned on. + * Turn on Data Saver when Battery Saver is turned on. + * If set to false, Data Saver WILL NOT be turned on when Battery Saver is turned on. + * If set to true, Data Saver WILL be turned on when Battery Saver is turned on. */ - private static final String KEY_ACTIVATE_DATASAVER_DISABLED = "datasaver_disabled"; + @VisibleForTesting + static final String KEY_ENABLE_DATASAVER = "enable_datasaver"; /** * {@code true} if the Policy should advertise to the rest of the system that battery saver * is enabled. This advertising could cause other system components to change their * behavior. This will not affect other policy flags and what they change. */ - private static final String KEY_ADVERTISE_IS_ENABLED = "advertise_is_enabled"; - - private static final String KEY_LAUNCH_BOOST_DISABLED = "launch_boost_disabled"; - private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor"; - private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred"; - private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred"; - private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby"; - private static final String KEY_FORCE_BACKGROUND_CHECK = "force_background_check"; - private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled"; - private static final String KEY_AOD_DISABLED = "aod_disabled"; + @VisibleForTesting + static final String KEY_ADVERTISE_IS_ENABLED = "advertise_is_enabled"; + + @VisibleForTesting + static final String KEY_DISABLE_LAUNCH_BOOST = "disable_launch_boost"; + @VisibleForTesting + static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor"; + @VisibleForTesting + static final String KEY_DEFER_FULL_BACKUP = "defer_full_backup"; + @VisibleForTesting + static final String KEY_DEFER_KEYVALUE_BACKUP = "defer_keyvalue_backup"; + @VisibleForTesting + static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby"; + @VisibleForTesting + static final String KEY_FORCE_BACKGROUND_CHECK = "force_background_check"; + @VisibleForTesting + static final String KEY_DISABLE_OPTIONAL_SENSORS = "disable_optional_sensors"; + @VisibleForTesting + static final String KEY_DISABLE_AOD = "disable_aod"; // Go into deep Doze as soon as the screen turns off. - private static final String KEY_QUICK_DOZE_ENABLED = "quick_doze_enabled"; - private static final String KEY_ENABLE_NIGHT_MODE = "enable_night_mode"; + @VisibleForTesting + static final String KEY_ENABLE_QUICK_DOZE = "enable_quick_doze"; + @VisibleForTesting + static final String KEY_ENABLE_NIGHT_MODE = "enable_night_mode"; private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i"; private static final String KEY_CPU_FREQ_NONINTERACTIVE = "cpufreq-n"; + private static final String KEY_SUFFIX_ADAPTIVE = "_adaptive"; + @VisibleForTesting static final Policy OFF_POLICY = new Policy( 1f, /* adjustBrightnessFactor */ @@ -172,10 +196,7 @@ public class BatterySaverPolicy extends ContentObserver { private String mDeviceSpecificSettingsSource; // For dump() only. @GuardedBy("mLock") - private String mAdaptiveSettings; - - @GuardedBy("mLock") - private String mAdaptiveDeviceSpecificSettings; + private DeviceConfig.Properties mLastDeviceConfigProperties; /** * A short string describing which battery saver is now enabled, which we dump in the eventlog. @@ -260,10 +281,6 @@ public class BatterySaverPolicy extends ContentObserver { Settings.Global.BATTERY_SAVER_CONSTANTS), false, this); mContentResolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this); - mContentResolver.registerContentObserver(Settings.Global.getUriFor( - Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS), false, this); - mContentResolver.registerContentObserver(Settings.Global.getUriFor( - Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS), false, this); final AccessibilityManager acm = mContext.getSystemService(AccessibilityManager.class); @@ -276,6 +293,10 @@ public class BatterySaverPolicy extends ContentObserver { mAutomotiveProjectionActive.initialize( uiModeManager.getActiveProjectionTypes() != UiModeManager.PROJECTION_TYPE_NONE); + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BATTERY_SAVER, + mContext.getMainExecutor(), this); + mLastDeviceConfigProperties = + DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BATTERY_SAVER); onChange(true, null); } @@ -304,7 +325,7 @@ public class BatterySaverPolicy extends ContentObserver { /** * Notifies listeners of a policy change on the handler thread only if the current policy level - * is not {@link POLICY_LEVEL_OFF}. + * is not {@link #POLICY_LEVEL_OFF}. */ private void maybeNotifyListenersOfPolicyChange() { final BatterySaverPolicyListener[] listeners; @@ -329,6 +350,55 @@ public class BatterySaverPolicy extends ContentObserver { refreshSettings(); } + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + // Need to get all of the flags atomically. + mLastDeviceConfigProperties = + DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BATTERY_SAVER); + Policy newAdaptivePolicy = null; + Policy newFullPolicy = null; + + boolean changed = false; + + synchronized (mLock) { + for (String name : properties.getKeyset()) { + if (name == null) { + continue; + } + if (name.endsWith(KEY_SUFFIX_ADAPTIVE)) { + if (newAdaptivePolicy == null) { + newAdaptivePolicy = Policy.fromSettings("", "", + mLastDeviceConfigProperties, KEY_SUFFIX_ADAPTIVE, + DEFAULT_ADAPTIVE_POLICY); + } + } else if (newFullPolicy == null) { + newFullPolicy = Policy.fromSettings(mSettings, mDeviceSpecificSettings, + mLastDeviceConfigProperties, null, DEFAULT_FULL_POLICY); + } + } + + if (newFullPolicy != null && !mFullPolicy.equals(newFullPolicy)) { + mFullPolicy = newFullPolicy; + changed |= (mPolicyLevel == POLICY_LEVEL_FULL); + } + + if (newAdaptivePolicy != null && !mAdaptivePolicy.equals(newAdaptivePolicy)) { + mDefaultAdaptivePolicy = newAdaptivePolicy; + // This will override any config set by an external source. This should be fine + // for now. + // TODO(119261320): make sure it doesn't override what's set externally + mAdaptivePolicy = mDefaultAdaptivePolicy; + changed |= (mPolicyLevel == POLICY_LEVEL_ADAPTIVE); + } + + updatePolicyDependenciesLocked(); + } + + if (changed) { + maybeNotifyListenersOfPolicyChange(); + } + } + private void refreshSettings() { synchronized (mLock) { // Load the non-device-specific setting. @@ -348,13 +418,7 @@ public class BatterySaverPolicy extends ContentObserver { mDeviceSpecificSettingsSource = "(overlay)"; } - final String adaptiveSetting = - getGlobalSetting(Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS); - final String adaptiveDeviceSpecificSetting = getGlobalSetting( - Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS); - - if (!updateConstantsLocked(setting, deviceSpecificSetting, - adaptiveSetting, adaptiveDeviceSpecificSetting)) { + if (!updateConstantsLocked(setting, deviceSpecificSetting)) { // Nothing of note changed. return; } @@ -365,47 +429,34 @@ public class BatterySaverPolicy extends ContentObserver { @GuardedBy("mLock") @VisibleForTesting - void updateConstantsLocked(final String setting, final String deviceSpecificSetting) { - updateConstantsLocked(setting, deviceSpecificSetting, "", ""); - } - /** @return true if the currently active policy changed. */ - private boolean updateConstantsLocked(String setting, String deviceSpecificSetting, - String adaptiveSetting, String adaptiveDeviceSpecificSetting) { + boolean updateConstantsLocked(String setting, String deviceSpecificSetting) { setting = TextUtils.emptyIfNull(setting); deviceSpecificSetting = TextUtils.emptyIfNull(deviceSpecificSetting); - adaptiveSetting = TextUtils.emptyIfNull(adaptiveSetting); - adaptiveDeviceSpecificSetting = TextUtils.emptyIfNull(adaptiveDeviceSpecificSetting); if (setting.equals(mSettings) - && deviceSpecificSetting.equals(mDeviceSpecificSettings) - && adaptiveSetting.equals(mAdaptiveSettings) - && adaptiveDeviceSpecificSetting.equals(mAdaptiveDeviceSpecificSettings)) { + && deviceSpecificSetting.equals(mDeviceSpecificSettings)) { return false; } mSettings = setting; mDeviceSpecificSettings = deviceSpecificSetting; - mAdaptiveSettings = adaptiveSetting; - mAdaptiveDeviceSpecificSettings = adaptiveDeviceSpecificSetting; if (DEBUG) { Slog.i(TAG, "mSettings=" + mSettings); Slog.i(TAG, "mDeviceSpecificSettings=" + mDeviceSpecificSettings); - Slog.i(TAG, "mAdaptiveSettings=" + mAdaptiveSettings); - Slog.i(TAG, "mAdaptiveDeviceSpecificSettings=" + mAdaptiveDeviceSpecificSettings); } boolean changed = false; Policy newFullPolicy = Policy.fromSettings(setting, deviceSpecificSetting, - DEFAULT_FULL_POLICY); + mLastDeviceConfigProperties, null, DEFAULT_FULL_POLICY); if (mPolicyLevel == POLICY_LEVEL_FULL && !mFullPolicy.equals(newFullPolicy)) { changed = true; } mFullPolicy = newFullPolicy; - mDefaultAdaptivePolicy = Policy.fromSettings(adaptiveSetting, adaptiveDeviceSpecificSetting, - DEFAULT_ADAPTIVE_POLICY); + mDefaultAdaptivePolicy = Policy.fromSettings("", "", + mLastDeviceConfigProperties, KEY_SUFFIX_ADAPTIVE, DEFAULT_ADAPTIVE_POLICY); if (mPolicyLevel == POLICY_LEVEL_ADAPTIVE && !mAdaptivePolicy.equals(mDefaultAdaptivePolicy)) { changed = true; @@ -508,7 +559,7 @@ public class BatterySaverPolicy extends ContentObserver { * {@code true} if full backup is deferred in battery saver mode. * * @see Settings.Global#BATTERY_SAVER_CONSTANTS - * @see #KEY_FULLBACKUP_DEFERRED + * @see #KEY_DEFER_FULL_BACKUP */ public final boolean deferFullBackup; @@ -516,7 +567,7 @@ public class BatterySaverPolicy extends ContentObserver { * {@code true} if key value backup is deferred in battery saver mode. * * @see Settings.Global#BATTERY_SAVER_CONSTANTS - * @see #KEY_KEYVALUE_DEFERRED + * @see #KEY_DEFER_KEYVALUE_BACKUP */ public final boolean deferKeyValueBackup; @@ -524,7 +575,7 @@ public class BatterySaverPolicy extends ContentObserver { * {@code true} if animation is disabled in battery saver mode. * * @see Settings.Global#BATTERY_SAVER_CONSTANTS - * @see #KEY_ANIMATION_DISABLED + * @see #KEY_DISABLE_ANIMATION */ public final boolean disableAnimation; @@ -548,7 +599,7 @@ public class BatterySaverPolicy extends ContentObserver { * in battery saver mode. * * @see Settings.Global#BATTERY_SAVER_CONSTANTS - * @see #KEY_SOUNDTRIGGER_DISABLED + * @see #KEY_DISABLE_SOUNDTRIGGER */ public final boolean disableSoundTrigger; @@ -556,7 +607,7 @@ public class BatterySaverPolicy extends ContentObserver { * {@code true} if vibration is disabled in battery saver mode. * * @see Settings.Global#BATTERY_SAVER_CONSTANTS - * @see #KEY_VIBRATION_DISABLED + * @see #KEY_DISABLE_VIBRATION */ public final boolean disableVibration; @@ -565,7 +616,7 @@ public class BatterySaverPolicy extends ContentObserver { * mode. * * @see Settings.Global#BATTERY_SAVER_CONSTANTS - * @see #KEY_ADJUST_BRIGHTNESS_DISABLED + * @see #KEY_ENABLE_BRIGHTNESS_ADJUSTMENT */ public final boolean enableAdjustBrightness; @@ -573,7 +624,7 @@ public class BatterySaverPolicy extends ContentObserver { * {@code true} if data saver should be turned on in battery saver mode. * * @see Settings.Global#BATTERY_SAVER_CONSTANTS - * @see #KEY_ACTIVATE_DATASAVER_DISABLED + * @see #KEY_ENABLE_DATASAVER */ public final boolean enableDataSaver; @@ -581,7 +632,7 @@ public class BatterySaverPolicy extends ContentObserver { * {@code true} if network policy firewall should be turned on in battery saver mode. * * @see Settings.Global#BATTERY_SAVER_CONSTANTS - * @see #KEY_ACTIVATE_FIREWALL_DISABLED + * @see #KEY_ENABLE_FIREWALL */ public final boolean enableFirewall; @@ -626,7 +677,7 @@ public class BatterySaverPolicy extends ContentObserver { * previously called gpsMode. * * @see Settings.Global#BATTERY_SAVER_CONSTANTS - * @see #KEY_GPS_MODE + * @see #KEY_LOCATION_MODE */ public final int locationMode; @@ -744,13 +795,17 @@ public class BatterySaverPolicy extends ContentObserver { ); } - static Policy fromSettings(String settings, String deviceSpecificSettings) { - return fromSettings(settings, deviceSpecificSettings, OFF_POLICY); + @VisibleForTesting + static Policy fromSettings(String settings, String deviceSpecificSettings, + DeviceConfig.Properties properties, String configSuffix) { + return fromSettings(settings, deviceSpecificSettings, properties, configSuffix, + OFF_POLICY); } - static Policy fromSettings(String settings, String deviceSpecificSettings, - Policy defaultPolicy) { + private static Policy fromSettings(String settings, String deviceSpecificSettings, + DeviceConfig.Properties properties, String configSuffix, Policy defaultPolicy) { final KeyValueListParser parser = new KeyValueListParser(','); + configSuffix = TextUtils.emptyIfNull(configSuffix); // Device-specific parameters. try { @@ -770,40 +825,63 @@ public class BatterySaverPolicy extends ContentObserver { Slog.wtf(TAG, "Bad battery saver constants: " + settings); } - float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, - defaultPolicy.adjustBrightnessFactor); - boolean advertiseIsEnabled = parser.getBoolean(KEY_ADVERTISE_IS_ENABLED, - defaultPolicy.advertiseIsEnabled); - boolean deferFullBackup = parser.getBoolean(KEY_FULLBACKUP_DEFERRED, - defaultPolicy.deferFullBackup); - boolean deferKeyValueBackup = parser.getBoolean(KEY_KEYVALUE_DEFERRED, - defaultPolicy.deferKeyValueBackup); - boolean disableAnimation = parser.getBoolean(KEY_ANIMATION_DISABLED, - defaultPolicy.disableAnimation); - boolean disableAod = parser.getBoolean(KEY_AOD_DISABLED, defaultPolicy.disableAod); - boolean disableLaunchBoost = parser.getBoolean(KEY_LAUNCH_BOOST_DISABLED, - defaultPolicy.disableLaunchBoost); - boolean disableOptionalSensors = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, - defaultPolicy.disableOptionalSensors); - boolean disableSoundTrigger = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, - defaultPolicy.disableSoundTrigger); - boolean disableVibrationConfig = parser.getBoolean(KEY_VIBRATION_DISABLED, - defaultPolicy.disableVibration); - boolean enableAdjustBrightness = !parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, - !defaultPolicy.enableAdjustBrightness); - boolean enableDataSaver = !parser.getBoolean(KEY_ACTIVATE_DATASAVER_DISABLED, - !defaultPolicy.enableDataSaver); - boolean enableFirewall = !parser.getBoolean(KEY_ACTIVATE_FIREWALL_DISABLED, - !defaultPolicy.enableFirewall); - boolean enableNightMode = parser.getBoolean(KEY_ENABLE_NIGHT_MODE, - defaultPolicy.enableNightMode); - boolean enableQuickDoze = parser.getBoolean(KEY_QUICK_DOZE_ENABLED, - defaultPolicy.enableQuickDoze); - boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, - defaultPolicy.forceAllAppsStandby); - boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK, - defaultPolicy.forceBackgroundCheck); - int locationMode = parser.getInt(KEY_GPS_MODE, defaultPolicy.locationMode); + // The Settings value overrides everything, since that will be set by the user. + // The DeviceConfig value takes second place, with the default as the last choice. + final float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, + properties.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix, + defaultPolicy.adjustBrightnessFactor)); + final boolean advertiseIsEnabled = parser.getBoolean(KEY_ADVERTISE_IS_ENABLED, + properties.getBoolean(KEY_ADVERTISE_IS_ENABLED + configSuffix, + defaultPolicy.advertiseIsEnabled)); + final boolean deferFullBackup = parser.getBoolean(KEY_DEFER_FULL_BACKUP, + properties.getBoolean(KEY_DEFER_FULL_BACKUP + configSuffix, + defaultPolicy.deferFullBackup)); + final boolean deferKeyValueBackup = parser.getBoolean(KEY_DEFER_KEYVALUE_BACKUP, + properties.getBoolean(KEY_DEFER_KEYVALUE_BACKUP + configSuffix, + defaultPolicy.deferKeyValueBackup)); + final boolean disableAnimation = parser.getBoolean(KEY_DISABLE_ANIMATION, + properties.getBoolean(KEY_DISABLE_ANIMATION + configSuffix, + defaultPolicy.disableAnimation)); + final boolean disableAod = parser.getBoolean(KEY_DISABLE_AOD, + properties.getBoolean(KEY_DISABLE_AOD + configSuffix, + defaultPolicy.disableAod)); + final boolean disableLaunchBoost = parser.getBoolean(KEY_DISABLE_LAUNCH_BOOST, + properties.getBoolean(KEY_DISABLE_LAUNCH_BOOST + configSuffix, + defaultPolicy.disableLaunchBoost)); + final boolean disableOptionalSensors = parser.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS, + properties.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS + configSuffix, + defaultPolicy.disableOptionalSensors)); + final boolean disableSoundTrigger = parser.getBoolean(KEY_DISABLE_SOUNDTRIGGER, + properties.getBoolean(KEY_DISABLE_SOUNDTRIGGER + configSuffix, + defaultPolicy.disableSoundTrigger)); + final boolean disableVibrationConfig = parser.getBoolean(KEY_DISABLE_VIBRATION, + properties.getBoolean(KEY_DISABLE_VIBRATION + configSuffix, + defaultPolicy.disableVibration)); + final boolean enableBrightnessAdjustment = parser.getBoolean( + KEY_ENABLE_BRIGHTNESS_ADJUSTMENT, + properties.getBoolean(KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix, + defaultPolicy.enableAdjustBrightness)); + final boolean enableDataSaver = parser.getBoolean(KEY_ENABLE_DATASAVER, + properties.getBoolean(KEY_ENABLE_DATASAVER + configSuffix, + defaultPolicy.enableDataSaver)); + final boolean enableFirewall = parser.getBoolean(KEY_ENABLE_FIREWALL, + properties.getBoolean(KEY_ENABLE_FIREWALL + configSuffix, + defaultPolicy.enableFirewall)); + final boolean enableNightMode = parser.getBoolean(KEY_ENABLE_NIGHT_MODE, + properties.getBoolean(KEY_ENABLE_NIGHT_MODE + configSuffix, + defaultPolicy.enableNightMode)); + final boolean enableQuickDoze = parser.getBoolean(KEY_ENABLE_QUICK_DOZE, + properties.getBoolean(KEY_ENABLE_QUICK_DOZE + configSuffix, + defaultPolicy.enableQuickDoze)); + final boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, + properties.getBoolean(KEY_FORCE_ALL_APPS_STANDBY + configSuffix, + defaultPolicy.forceAllAppsStandby)); + final boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK, + properties.getBoolean(KEY_FORCE_BACKGROUND_CHECK + configSuffix, + defaultPolicy.forceBackgroundCheck)); + final int locationMode = parser.getInt(KEY_LOCATION_MODE, + properties.getInt(KEY_LOCATION_MODE + configSuffix, + defaultPolicy.locationMode)); return new Policy( adjustBrightnessFactor, @@ -817,7 +895,7 @@ public class BatterySaverPolicy extends ContentObserver { disableSoundTrigger, /* disableVibration */ disableVibrationConfig, - enableAdjustBrightness, + enableBrightnessAdjustment, enableDataSaver, enableFirewall, enableNightMode, @@ -1031,90 +1109,94 @@ public class BatterySaverPolicy extends ContentObserver { } public void dump(PrintWriter pw) { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + synchronized (mLock) { - pw.println(); - mBatterySavingStats.dump(pw, ""); - - pw.println(); - pw.println("Battery saver policy (*NOTE* they only apply when battery saver is ON):"); - pw.println(" Settings: " + Settings.Global.BATTERY_SAVER_CONSTANTS); - pw.println(" value: " + mSettings); - pw.println(" Settings: " + mDeviceSpecificSettingsSource); - pw.println(" value: " + mDeviceSpecificSettings); - - pw.println(" Adaptive Settings: " + Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS); - pw.println(" value: " + mAdaptiveSettings); - pw.println(" Adaptive Device Specific Settings: " - + Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS); - pw.println(" value: " + mAdaptiveDeviceSpecificSettings); - - pw.println(" mAccessibilityEnabled=" + mAccessibilityEnabled.get()); - pw.println(" mAutomotiveProjectionActive=" + mAutomotiveProjectionActive.get()); - pw.println(" mPolicyLevel=" + mPolicyLevel); - - dumpPolicyLocked(pw, " ", "full", mFullPolicy); - dumpPolicyLocked(pw, " ", "default adaptive", mDefaultAdaptivePolicy); - dumpPolicyLocked(pw, " ", "current adaptive", mAdaptivePolicy); - dumpPolicyLocked(pw, " ", "effective", mEffectivePolicyRaw); + ipw.println(); + mBatterySavingStats.dump(ipw); + + ipw.println(); + ipw.println("Battery saver policy (*NOTE* they only apply when battery saver is ON):"); + ipw.increaseIndent(); + ipw.println("Settings: " + Settings.Global.BATTERY_SAVER_CONSTANTS); + ipw.increaseIndent(); + ipw.println("value: " + mSettings); + ipw.decreaseIndent(); + ipw.println("Settings: " + mDeviceSpecificSettingsSource); + ipw.increaseIndent(); + ipw.println("value: " + mDeviceSpecificSettings); + ipw.decreaseIndent(); + ipw.println("DeviceConfig: " + DeviceConfig.NAMESPACE_BATTERY_SAVER); + ipw.increaseIndent(); + final Set<String> keys = mLastDeviceConfigProperties.getKeyset(); + if (keys.size() == 0) { + ipw.println("N/A"); + } else { + for (final String key : keys) { + ipw.print(key); + ipw.print(": "); + ipw.println(mLastDeviceConfigProperties.getString(key, null)); + } + } + + ipw.println("mAccessibilityEnabled=" + mAccessibilityEnabled.get()); + ipw.println("mAutomotiveProjectionActive=" + mAutomotiveProjectionActive.get()); + ipw.println("mPolicyLevel=" + mPolicyLevel); + + dumpPolicyLocked(ipw, "full", mFullPolicy); + dumpPolicyLocked(ipw, "default adaptive", mDefaultAdaptivePolicy); + dumpPolicyLocked(ipw, "current adaptive", mAdaptivePolicy); + dumpPolicyLocked(ipw, "effective", mEffectivePolicyRaw); + + ipw.decreaseIndent(); } } - private void dumpPolicyLocked(PrintWriter pw, String indent, String label, Policy p) { + private void dumpPolicyLocked(IndentingPrintWriter pw, String label, Policy p) { pw.println(); - pw.print(indent); pw.println("Policy '" + label + "'"); - pw.print(indent); - pw.println(" " + KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled); - pw.print(indent); - pw.println(" " + KEY_VIBRATION_DISABLED + "=" + p.disableVibration); - pw.print(indent); - pw.println(" " + KEY_ANIMATION_DISABLED + "=" + p.disableAnimation); - pw.print(indent); - pw.println(" " + KEY_FULLBACKUP_DEFERRED + "=" + p.deferFullBackup); - pw.print(indent); - pw.println(" " + KEY_KEYVALUE_DEFERRED + "=" + p.deferKeyValueBackup); - pw.print(indent); - pw.println(" " + KEY_ACTIVATE_FIREWALL_DISABLED + "=" + !p.enableFirewall); - pw.print(indent); - pw.println(" " + KEY_ACTIVATE_DATASAVER_DISABLED + "=" + !p.enableDataSaver); - pw.print(indent); - pw.println(" " + KEY_LAUNCH_BOOST_DISABLED + "=" + p.disableLaunchBoost); - pw.println( - " " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + !p.enableAdjustBrightness); - pw.print(indent); - pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + p.adjustBrightnessFactor); - pw.print(indent); - pw.println(" " + KEY_GPS_MODE + "=" + p.locationMode); - pw.print(indent); - pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY + "=" + p.forceAllAppsStandby); - pw.print(indent); - pw.println(" " + KEY_FORCE_BACKGROUND_CHECK + "=" + p.forceBackgroundCheck); - pw.println( - " " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + p.disableOptionalSensors); - pw.print(indent); - pw.println(" " + KEY_AOD_DISABLED + "=" + p.disableAod); - pw.print(indent); - pw.println(" " + KEY_SOUNDTRIGGER_DISABLED + "=" + p.disableSoundTrigger); - pw.print(indent); - pw.println(" " + KEY_QUICK_DOZE_ENABLED + "=" + p.enableQuickDoze); - pw.print(indent); - pw.println(" " + KEY_ENABLE_NIGHT_MODE + "=" + p.enableNightMode); - - pw.print(" Interactive File values:\n"); - dumpMap(pw, " ", p.filesForInteractive); + pw.increaseIndent(); + pw.println(KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled); + pw.println(KEY_DISABLE_VIBRATION + "=" + p.disableVibration); + pw.println(KEY_DISABLE_ANIMATION + "=" + p.disableAnimation); + pw.println(KEY_DEFER_FULL_BACKUP + "=" + p.deferFullBackup); + pw.println(KEY_DEFER_KEYVALUE_BACKUP + "=" + p.deferKeyValueBackup); + pw.println(KEY_ENABLE_FIREWALL + "=" + p.enableFirewall); + pw.println(KEY_ENABLE_DATASAVER + "=" + p.enableDataSaver); + pw.println(KEY_DISABLE_LAUNCH_BOOST + "=" + p.disableLaunchBoost); + pw.println(KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + "=" + p.enableAdjustBrightness); + pw.println(KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + p.adjustBrightnessFactor); + pw.println(KEY_LOCATION_MODE + "=" + p.locationMode); + pw.println(KEY_FORCE_ALL_APPS_STANDBY + "=" + p.forceAllAppsStandby); + pw.println(KEY_FORCE_BACKGROUND_CHECK + "=" + p.forceBackgroundCheck); + pw.println(KEY_DISABLE_OPTIONAL_SENSORS + "=" + p.disableOptionalSensors); + pw.println(KEY_DISABLE_AOD + "=" + p.disableAod); + pw.println(KEY_DISABLE_SOUNDTRIGGER + "=" + p.disableSoundTrigger); + pw.println(KEY_ENABLE_QUICK_DOZE + "=" + p.enableQuickDoze); + pw.println(KEY_ENABLE_NIGHT_MODE + "=" + p.enableNightMode); + + pw.println("Interactive File values:"); + pw.increaseIndent(); + dumpMap(pw, p.filesForInteractive); + pw.decreaseIndent(); pw.println(); - pw.print(" Noninteractive File values:\n"); - dumpMap(pw, " ", p.filesForNoninteractive); + pw.println("Noninteractive File values:"); + pw.increaseIndent(); + dumpMap(pw, p.filesForNoninteractive); + pw.decreaseIndent(); + + // Decrease from indent right after "Policy" line + pw.decreaseIndent(); } - private void dumpMap(PrintWriter pw, String prefix, ArrayMap<String, String> map) { + private void dumpMap(PrintWriter pw, ArrayMap<String, String> map) { if (map == null) { + pw.println("N/A"); return; } final int size = map.size(); for (int i = 0; i < size; i++) { - pw.print(prefix); pw.print(map.keyAt(i)); pw.print(": '"); pw.print(map.valueAt(i)); diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index af14d84d12b8..21500f649099 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -34,6 +34,7 @@ import android.os.PowerManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -888,70 +889,75 @@ public class BatterySaverStateMachine { } public void dump(PrintWriter pw) { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + + ipw.println(); + ipw.println("Battery saver state machine:"); + ipw.increaseIndent(); synchronized (mLock) { - pw.println(); - pw.println("Battery saver state machine:"); - - pw.print(" Enabled="); - pw.println(mBatterySaverController.isEnabled()); - pw.print(" full="); - pw.println(mBatterySaverController.isFullEnabled()); - pw.print(" adaptive="); - pw.print(mBatterySaverController.isAdaptiveEnabled()); + ipw.print("Enabled="); + ipw.println(mBatterySaverController.isEnabled()); + ipw.increaseIndent(); + ipw.print("full="); + ipw.println(mBatterySaverController.isFullEnabled()); + ipw.print("adaptive="); + ipw.print(mBatterySaverController.isAdaptiveEnabled()); if (mBatterySaverController.isAdaptiveEnabled()) { - pw.print(" (advertise="); - pw.print( + ipw.print(" (advertise="); + ipw.print( mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled()); - pw.print(")"); + ipw.print(")"); } - pw.println(); - pw.print(" mState="); - pw.println(mState); - - pw.print(" mLastChangedIntReason="); - pw.println(mLastChangedIntReason); - pw.print(" mLastChangedStrReason="); - pw.println(mLastChangedStrReason); - - pw.print(" mBootCompleted="); - pw.println(mBootCompleted); - pw.print(" mSettingsLoaded="); - pw.println(mSettingsLoaded); - pw.print(" mBatteryStatusSet="); - pw.println(mBatteryStatusSet); - - pw.print(" mIsPowered="); - pw.println(mIsPowered); - pw.print(" mBatteryLevel="); - pw.println(mBatteryLevel); - pw.print(" mIsBatteryLevelLow="); - pw.println(mIsBatteryLevelLow); - - pw.print(" mSettingAutomaticBatterySaver="); - pw.println(mSettingAutomaticBatterySaver); - pw.print(" mSettingBatterySaverEnabled="); - pw.println(mSettingBatterySaverEnabled); - pw.print(" mSettingBatterySaverEnabledSticky="); - pw.println(mSettingBatterySaverEnabledSticky); - pw.print(" mSettingBatterySaverStickyAutoDisableEnabled="); - pw.println(mSettingBatterySaverStickyAutoDisableEnabled); - pw.print(" mSettingBatterySaverStickyAutoDisableThreshold="); - pw.println(mSettingBatterySaverStickyAutoDisableThreshold); - pw.print(" mSettingBatterySaverTriggerThreshold="); - pw.println(mSettingBatterySaverTriggerThreshold); - pw.print(" mBatterySaverStickyBehaviourDisabled="); - pw.println(mBatterySaverStickyBehaviourDisabled); - - pw.print(" mDynamicPowerSavingsDefaultDisableThreshold="); - pw.println(mDynamicPowerSavingsDefaultDisableThreshold); - pw.print(" mDynamicPowerSavingsDisableThreshold="); - pw.println(mDynamicPowerSavingsDisableThreshold); - pw.print(" mDynamicPowerSavingsEnableBatterySaver="); - pw.println(mDynamicPowerSavingsEnableBatterySaver); - - pw.print(" mLastAdaptiveBatterySaverChangedExternallyElapsed="); - pw.println(mLastAdaptiveBatterySaverChangedExternallyElapsed); + ipw.decreaseIndent(); + ipw.println(); + ipw.print("mState="); + ipw.println(mState); + + ipw.print("mLastChangedIntReason="); + ipw.println(mLastChangedIntReason); + ipw.print("mLastChangedStrReason="); + ipw.println(mLastChangedStrReason); + + ipw.print("mBootCompleted="); + ipw.println(mBootCompleted); + ipw.print("mSettingsLoaded="); + ipw.println(mSettingsLoaded); + ipw.print("mBatteryStatusSet="); + ipw.println(mBatteryStatusSet); + + ipw.print("mIsPowered="); + ipw.println(mIsPowered); + ipw.print("mBatteryLevel="); + ipw.println(mBatteryLevel); + ipw.print("mIsBatteryLevelLow="); + ipw.println(mIsBatteryLevelLow); + + ipw.print("mSettingAutomaticBatterySaver="); + ipw.println(mSettingAutomaticBatterySaver); + ipw.print("mSettingBatterySaverEnabled="); + ipw.println(mSettingBatterySaverEnabled); + ipw.print("mSettingBatterySaverEnabledSticky="); + ipw.println(mSettingBatterySaverEnabledSticky); + ipw.print("mSettingBatterySaverStickyAutoDisableEnabled="); + ipw.println(mSettingBatterySaverStickyAutoDisableEnabled); + ipw.print("mSettingBatterySaverStickyAutoDisableThreshold="); + ipw.println(mSettingBatterySaverStickyAutoDisableThreshold); + ipw.print("mSettingBatterySaverTriggerThreshold="); + ipw.println(mSettingBatterySaverTriggerThreshold); + ipw.print("mBatterySaverStickyBehaviourDisabled="); + ipw.println(mBatterySaverStickyBehaviourDisabled); + + ipw.print("mDynamicPowerSavingsDefaultDisableThreshold="); + ipw.println(mDynamicPowerSavingsDefaultDisableThreshold); + ipw.print("mDynamicPowerSavingsDisableThreshold="); + ipw.println(mDynamicPowerSavingsDisableThreshold); + ipw.print("mDynamicPowerSavingsEnableBatterySaver="); + ipw.println(mDynamicPowerSavingsEnableBatterySaver); + + ipw.print("mLastAdaptiveBatterySaverChangedExternallyElapsed="); + ipw.println(mLastAdaptiveBatterySaverChangedExternallyElapsed); } + ipw.decreaseIndent(); } public void dumpProto(ProtoOutputStream proto, long tag) { diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java index 05695d919910..a7be2677cf23 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java @@ -17,6 +17,7 @@ package com.android.server.power.batterysaver; import android.os.BatteryManagerInternal; import android.os.SystemClock; +import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -26,7 +27,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.EventLogTags; import com.android.server.LocalServices; -import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; @@ -391,18 +391,15 @@ public class BatterySavingStats { stat.endTime = 0; } - public void dump(PrintWriter pw, String indent) { - synchronized (mLock) { - pw.print(indent); - pw.println("Battery saving stats:"); - - indent = indent + " "; + public void dump(IndentingPrintWriter pw) { + pw.println("Battery saving stats:"); + pw.increaseIndent(); + synchronized (mLock) { final long now = System.currentTimeMillis(); final long nowElapsed = injectCurrentTime(); final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - pw.print(indent); pw.print("Battery Saver is currently: "); switch (BatterySaverState.fromIndex(mCurrentState)) { case BatterySaverState.OFF: @@ -416,9 +413,8 @@ public class BatterySavingStats { break; } + pw.increaseIndent(); if (mLastBatterySaverEnabledTime > 0) { - pw.print(indent); - pw.print(" "); pw.print("Last ON time: "); pw.print(sdf.format(new Date(now - nowElapsed + mLastBatterySaverEnabledTime))); pw.print(" "); @@ -427,8 +423,6 @@ public class BatterySavingStats { } if (mLastBatterySaverDisabledTime > 0) { - pw.print(indent); - pw.print(" "); pw.print("Last OFF time: "); pw.print(sdf.format(new Date(now - nowElapsed + mLastBatterySaverDisabledTime))); pw.print(" "); @@ -436,14 +430,10 @@ public class BatterySavingStats { pw.println(); } - pw.print(indent); - pw.print(" "); pw.print("Times full enabled: "); pw.println(mBatterySaverEnabledCount); if (mLastAdaptiveBatterySaverEnabledTime > 0) { - pw.print(indent); - pw.print(" "); pw.print("Last ADAPTIVE ON time: "); pw.print(sdf.format( new Date(now - nowElapsed + mLastAdaptiveBatterySaverEnabledTime))); @@ -452,8 +442,6 @@ public class BatterySavingStats { pw.println(); } if (mLastAdaptiveBatterySaverDisabledTime > 0) { - pw.print(indent); - pw.print(" "); pw.print("Last ADAPTIVE OFF time: "); pw.print(sdf.format( new Date(now - nowElapsed + mLastAdaptiveBatterySaverDisabledTime))); @@ -461,39 +449,36 @@ public class BatterySavingStats { TimeUtils.formatDuration(mLastAdaptiveBatterySaverDisabledTime, nowElapsed, pw); pw.println(); } - pw.print(indent); - pw.print(" "); pw.print("Times adaptive enabled: "); pw.println(mAdaptiveBatterySaverEnabledCount); + pw.decreaseIndent(); pw.println(); - pw.print(indent); pw.println("Drain stats:"); - pw.print(indent); pw.println(" Battery saver OFF ON"); - dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + dumpLineLocked(pw, InteractiveState.NON_INTERACTIVE, "NonIntr", DozeState.NOT_DOZING, "NonDoze"); - dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + dumpLineLocked(pw, InteractiveState.INTERACTIVE, " Intr", DozeState.NOT_DOZING, " "); - dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + dumpLineLocked(pw, InteractiveState.NON_INTERACTIVE, "NonIntr", DozeState.DEEP, "Deep "); - dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + dumpLineLocked(pw, InteractiveState.INTERACTIVE, " Intr", DozeState.DEEP, " "); - dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + dumpLineLocked(pw, InteractiveState.NON_INTERACTIVE, "NonIntr", DozeState.LIGHT, "Light "); - dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + dumpLineLocked(pw, InteractiveState.INTERACTIVE, " Intr", DozeState.LIGHT, " "); } + pw.decreaseIndent(); } - private void dumpLineLocked(PrintWriter pw, String indent, + private void dumpLineLocked(IndentingPrintWriter pw, int interactiveState, String interactiveLabel, int dozeState, String dozeLabel) { - pw.print(indent); pw.print(dozeLabel); pw.print(" "); pw.print(interactiveLabel); diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index ab6ada2f85f7..eb15c808f512 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -71,7 +71,6 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.UserManagerInternal; -import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; @@ -83,7 +82,6 @@ import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; /** * Service for role management. @@ -162,12 +160,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C LocalServices.addService(RoleManagerInternal.class, new Internal()); - PermissionManagerServiceInternal permissionManagerInternal = - LocalServices.getService(PermissionManagerServiceInternal.class); - permissionManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider()); - permissionManagerInternal.setDefaultDialerProvider(new DefaultDialerProvider()); - permissionManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider()); - registerUserRemovedReceiver(); } @@ -657,12 +649,78 @@ public class RoleManagerService extends SystemService implements RoleUserState.C resultReceiver); } + @Nullable + @Override + public String getBrowserRoleHolder(@UserIdInt int userId) { + final int callingUid = Binder.getCallingUid(); + if (UserHandle.getUserId(callingUid) != userId) { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); + } + final PackageManagerInternal packageManager = LocalServices.getService( + PackageManagerInternal.class); + if (packageManager.getInstantAppPackageName(callingUid) != null) { + return null; + } + + final long identity = Binder.clearCallingIdentity(); + try { + return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_BROWSER, + userId)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) { + final Context context = getContext(); + context.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + if (UserHandle.getCallingUserId() != userId) { + context.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); + } + + if (!mUserManagerInternal.exists(userId)) { + return false; + } + + final AndroidFuture<Void> future = new AndroidFuture<>(); + final RemoteCallback callback = new RemoteCallback(result -> { + boolean successful = result != null; + if (successful) { + future.complete(null); + } else { + future.completeExceptionally(new RuntimeException()); + } + }); + final long identity = Binder.clearCallingIdentity(); + try { + if (packageName != null) { + addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, userId, callback); + } else { + clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, userId, callback); + } + try { + future.get(5, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Slog.e(LOG_TAG, "Exception while setting default browser: " + packageName, e); + return false; + } + } finally { + Binder.restoreCallingIdentity(identity); + } + + return true; + } + @Override - public String getDefaultSmsPackage(int userId) { + public String getSmsRoleHolder(int userId) { final long identity = Binder.clearCallingIdentity(); try { - return CollectionUtils.firstOrNull( - getRoleHoldersAsUser(RoleManager.ROLE_SMS, userId)); + return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_SMS, + userId)); } finally { Binder.restoreCallingIdentity(identity); } @@ -718,100 +776,4 @@ public class RoleManagerService extends SystemService implements RoleUserState.C return getOrCreateUserState(userId).getRolesAndHolders(); } } - - private class DefaultBrowserProvider implements - PermissionManagerServiceInternal.DefaultBrowserProvider { - - @Nullable - @Override - public String getDefaultBrowser(@UserIdInt int userId) { - return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders( - RoleManager.ROLE_BROWSER)); - } - - @Override - public boolean setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId) { - AndroidFuture<Void> future = new AndroidFuture<>(); - RemoteCallback callback = new RemoteCallback(result -> { - boolean successful = result != null; - if (successful) { - future.complete(null); - } else { - future.completeExceptionally(new RuntimeException()); - } - }); - if (packageName != null) { - getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, - packageName, 0, callback); - } else { - getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, - callback); - } - try { - future.get(5, TimeUnit.SECONDS); - return true; - } catch (InterruptedException | ExecutionException | TimeoutException e) { - Slog.e(LOG_TAG, "Exception while setting default browser: " + packageName, e); - return false; - } - } - - @Override - public void setDefaultBrowserAsync(@Nullable String packageName, @UserIdInt int userId) { - RemoteCallback callback = new RemoteCallback(result -> { - boolean successful = result != null; - if (!successful) { - Slog.e(LOG_TAG, "Failed to set default browser: " + packageName); - } - }); - if (packageName != null) { - getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, - packageName, 0, callback); - } else { - getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, - callback); - } - } - } - - private class DefaultDialerProvider implements - PermissionManagerServiceInternal.DefaultDialerProvider { - - @Nullable - @Override - public String getDefaultDialer(@UserIdInt int userId) { - return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders( - RoleManager.ROLE_DIALER)); - } - } - - private class DefaultHomeProvider implements - PermissionManagerServiceInternal.DefaultHomeProvider { - - @Nullable - @Override - public String getDefaultHome(@UserIdInt int userId) { - return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders( - RoleManager.ROLE_HOME)); - } - - @Override - public void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId, - @NonNull Consumer<Boolean> callback) { - RemoteCallback remoteCallback = new RemoteCallback(result -> { - boolean successful = result != null; - if (!successful) { - Slog.e(LOG_TAG, "Failed to set default home: " + packageName); - } - callback.accept(successful); - }); - if (packageName != null) { - getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_HOME, - packageName, 0, remoteCallback); - } else { - getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0, - remoteCallback); - } - } - } } diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 63ed416f2859..d9b67024018b 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -581,9 +581,18 @@ class Rollback { ParcelFileDescriptor.MODE_READ_ONLY)) { final long token = Binder.clearCallingIdentity(); try { - session.write(packageCodePath.getName(), 0, - packageCodePath.length(), - fd); + boolean fallbackToCopy = false; + try { + // Populate apk/apex files using hard links to avoid copy + session.stageViaHardLink(packageCodePath.getAbsolutePath()); + } catch (Exception ignore) { + fallbackToCopy = true; + } + if (fallbackToCopy) { + session.write(packageCodePath.getName(), 0, + packageCodePath.length(), + fd); + } } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index ee9694f90216..ee0e5ba916b9 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -256,11 +256,6 @@ public class SliceManagerService extends ISliceManager.Stub { } } } - // Fallback to allowing uri permissions through. - if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - == PERMISSION_GRANTED) { - return PackageManager.PERMISSION_GRANTED; - } return PackageManager.PERMISSION_DENIED; } 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 d3b1ac6e6096..cf20cf4c0c9f 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -502,6 +502,8 @@ public class StatsPullAtomService extends SystemService { synchronized (mProcessSystemIonHeapSizeLock) { return pullProcessSystemIonHeapSizeLocked(atomTag, data); } + case FrameworkStatsLog.SYSTEM_MEMORY: + return pullSystemMemory(atomTag, data); case FrameworkStatsLog.TEMPERATURE: synchronized (mTemperatureLock) { return pullTemperatureLocked(atomTag, data); @@ -796,6 +798,7 @@ public class StatsPullAtomService extends SystemService { registerSystemIonHeapSize(); registerIonHeapSize(); registerProcessSystemIonHeapSize(); + registerSystemMemory(); registerTemperature(); registerCoolingDevice(); registerBinderCallsStats(); @@ -1913,6 +1916,30 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private void registerSystemMemory() { + int tagId = FrameworkStatsLog.SYSTEM_MEMORY; + mStatsManager.setPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } + + int pullSystemMemory(int atomTag, List<StatsEvent> pulledData) { + SystemMemoryUtil.Metrics metrics = SystemMemoryUtil.getMetrics(); + pulledData.add( + FrameworkStatsLog.buildStatsEvent( + atomTag, + metrics.unreclaimableSlabKb, + metrics.vmallocUsedKb, + metrics.pageTablesKb, + metrics.kernelStackKb, + metrics.totalIonKb, + metrics.unaccountedKb)); + return StatsManager.PULL_SUCCESS; + } + private void registerTemperature() { int tagId = FrameworkStatsLog.TEMPERATURE; mStatsManager.setPullAtomCallback( diff --git a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java new file mode 100644 index 000000000000..99fc7c11c5b3 --- /dev/null +++ b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java @@ -0,0 +1,77 @@ +/* + * 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.stats.pull; + +import android.os.Debug; + +/** + * Snapshots system-wide memory stats and computes unaccounted memory. + * Thread-safe. + */ +final class SystemMemoryUtil { + private SystemMemoryUtil() {} + + static Metrics getMetrics() { + int totalIonKb = (int) Debug.getIonHeapsSizeKb(); + + long[] mInfos = new long[Debug.MEMINFO_COUNT]; + Debug.getMemInfo(mInfos); + + long kReclaimableKb = mInfos[Debug.MEMINFO_KRECLAIMABLE]; + // Note: MEMINFO_KRECLAIMABLE includes MEMINFO_SLAB_RECLAIMABLE and ION pools. + // Fall back to using MEMINFO_SLAB_RECLAIMABLE in case of older kernels that do + // not include KReclaimable meminfo field. + if (kReclaimableKb == 0) { + kReclaimableKb = mInfos[Debug.MEMINFO_SLAB_RECLAIMABLE]; + } + + long accountedKb = mInfos[Debug.MEMINFO_FREE] + + mInfos[Debug.MEMINFO_ZRAM_TOTAL] + + mInfos[Debug.MEMINFO_BUFFERS] + + mInfos[Debug.MEMINFO_ACTIVE] + + mInfos[Debug.MEMINFO_INACTIVE] + + mInfos[Debug.MEMINFO_UNEVICTABLE] + + mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE] + + kReclaimableKb + + mInfos[Debug.MEMINFO_VM_ALLOC_USED] + + mInfos[Debug.MEMINFO_PAGE_TABLES] + + Math.max(totalIonKb, 0); + + if (!Debug.isVmapStack()) { + // See b/146088882 + accountedKb += mInfos[Debug.MEMINFO_KERNEL_STACK]; + } + + Metrics result = new Metrics(); + result.unreclaimableSlabKb = (int) mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE]; + result.vmallocUsedKb = (int) mInfos[Debug.MEMINFO_VM_ALLOC_USED]; + result.pageTablesKb = (int) mInfos[Debug.MEMINFO_PAGE_TABLES]; + result.kernelStackKb = (int) mInfos[Debug.MEMINFO_KERNEL_STACK]; + result.totalIonKb = totalIonKb; + result.unaccountedKb = (int) (mInfos[Debug.MEMINFO_TOTAL] - accountedKb); + return result; + } + + static final class Metrics { + public int unreclaimableSlabKb; + public int vmallocUsedKb; + public int pageTablesKb; + public int kernelStackKb; + public int totalIonKb; + public int unaccountedKb; + } +} diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java index 52ad893a9ace..f0c96e18930a 100644 --- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java +++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java @@ -42,7 +42,7 @@ import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.UserManagerService; -import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; /** * Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup @@ -65,8 +65,8 @@ public class TelecomLoaderService extends SystemService { ServiceManager.addService(Context.TELECOM_SERVICE, telecomService.asBinder()); synchronized (mLock) { - final PermissionManagerServiceInternal permissionManager = - LocalServices.getService(PermissionManagerServiceInternal.class); + final LegacyPermissionManagerInternal permissionManager = + LocalServices.getService(LegacyPermissionManagerInternal.class); if (mDefaultSimCallManagerRequests != null) { if (mDefaultSimCallManagerRequests != null) { TelecomManager telecomManager = @@ -165,8 +165,8 @@ public class TelecomLoaderService extends SystemService { private void registerDefaultAppProviders() { - final PermissionManagerServiceInternal permissionManager = - LocalServices.getService(PermissionManagerServiceInternal.class); + final LegacyPermissionManagerInternal permissionManager = + LocalServices.getService(LegacyPermissionManagerInternal.class); // Set a callback for the permission grant policy to query the default sms app. permissionManager.setSmsAppPackagesProvider(userId -> { @@ -244,15 +244,16 @@ public class TelecomLoaderService extends SystemService { } private void updateSimCallManagerPermissions(int userId) { - final PermissionManagerServiceInternal permissionManager = - LocalServices.getService(PermissionManagerServiceInternal.class); + final LegacyPermissionManagerInternal permissionManager = + LocalServices.getService(LegacyPermissionManagerInternal.class); TelecomManager telecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId); if (phoneAccount != null) { Slog.i(TAG, "updating sim call manager permissions for userId:" + userId); String packageName = phoneAccount.getComponentName().getPackageName(); - permissionManager.grantDefaultPermissionsToDefaultSimCallManager(packageName, userId); + permissionManager.grantDefaultPermissionsToDefaultSimCallManager(packageName, + userId); } } } diff --git a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java index 118899add968..1867ee207958 100644 --- a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java +++ b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java @@ -61,7 +61,7 @@ import java.util.StringTokenizer; */ public final class GeolocationTimeZoneSuggestion { - @NonNull private final List<String> mZoneIds; + @Nullable private final List<String> mZoneIds; @Nullable private ArrayList<String> mDebugInfo; public GeolocationTimeZoneSuggestion(@Nullable List<String> zoneIds) { diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java index d3c9b3bbe7f5..72556a75a4b5 100644 --- a/services/core/java/com/android/server/tv/PersistentDataStore.java +++ b/services/core/java/com/android/server/tv/PersistentDataStore.java @@ -30,23 +30,17 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -246,12 +240,7 @@ final class PersistentDataStore { if (parser.getName().equals(TAG_BLOCKED_RATINGS)) { loadBlockedRatingsFromXml(parser); } else if (parser.getName().equals(TAG_PARENTAL_CONTROLS)) { - String enabled = parser.getAttributeValue(null, ATTR_ENABLED); - if (TextUtils.isEmpty(enabled)) { - throw new XmlPullParserException( - "Missing " + ATTR_ENABLED + " attribute on " + TAG_PARENTAL_CONTROLS); - } - mParentalControlsEnabled = Boolean.parseBoolean(enabled); + mParentalControlsEnabled = parser.getAttributeBoolean(null, ATTR_ENABLED); } } } diff --git a/services/core/java/com/android/server/tv/TEST_MAPPING b/services/core/java/com/android/server/tv/TEST_MAPPING new file mode 100644 index 000000000000..f718f909fecb --- /dev/null +++ b/services/core/java/com/android/server/tv/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "cts/tests/tests/tv" + } + ] +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 510893b2940b..1754e593914d 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -84,6 +84,7 @@ import android.view.InputChannel; import android.view.Surface; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; @@ -768,6 +769,7 @@ public final class TvInputManagerService extends SystemService { SessionState sessionState = userState.sessionStateMap.remove(sessionToken); if (sessionState == null) { + Slog.e(TAG, "sessionState null, no more remove session action!"); return; } @@ -1441,8 +1443,8 @@ public final class TvInputManagerService extends SystemService { if (sessionState != null) { int state = surface == null ? - FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED - : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED; + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED + : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED; logTuneStateChanged(state, sessionState, TvInputManagerService.getTvInputState(sessionState, userState)); } @@ -2525,8 +2527,16 @@ public final class TvInputManagerService extends SystemService { ClientState clientState = userState.clientStateMap.get(clientToken); if (clientState != null) { while (clientState.sessionTokens.size() > 0) { + IBinder sessionToken = clientState.sessionTokens.get(0); releaseSessionLocked( - clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId); + sessionToken, Process.SYSTEM_UID, userId); + // the releaseSessionLocked function may return before the sessionToken + // is removed if the related sessionState is null. So need to check again + // to avoid death curculation. + if (clientState.sessionTokens.contains(sessionToken)) { + Slog.d(TAG, "remove sessionToken " + sessionToken + " for " + clientToken); + clientState.sessionTokens.remove(sessionToken); + } } } clientToken = null; @@ -2963,15 +2973,7 @@ public final class TvInputManagerService extends SystemService { getUserStateLocked(mCurrentUserId)); try { mSessionState.client.onVideoUnavailable(reason, mSessionState.seq); - int loggedReason = reason + FrameworkStatsLog - .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; - if (loggedReason < FrameworkStatsLog - .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN - || loggedReason > FrameworkStatsLog - .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) { - loggedReason = FrameworkStatsLog - .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; - } + int loggedReason = getVideoUnavailableReasonForStatsd(reason); logTuneStateChanged(loggedReason, mSessionState, tvInputState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoUnavailable", e); @@ -3158,6 +3160,21 @@ public final class TvInputManagerService extends SystemService { } } + @VisibleForTesting + static int getVideoUnavailableReasonForStatsd( + @TvInputManager.VideoUnavailableReason int reason) { + int loggedReason = reason + FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; + if (loggedReason < FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN + || loggedReason > FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) { + loggedReason = FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; + } + return loggedReason; + } + private UserState getUserStateLocked(int userId) { return mUserStates.get(userId); } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java index beb11ed4ea0c..7f49eead19fa 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java @@ -192,8 +192,9 @@ public class UseCasePriorityHints { } } - private int readAttributeToInt(String attributeName, TypedXmlPullParser parser) { - return Integer.valueOf(parser.getAttributeValue(null, attributeName)); + private int readAttributeToInt(String attributeName, TypedXmlPullParser parser) + throws XmlPullParserException { + return parser.getAttributeInt(null, attributeName); } private void addNewUseCasePriority(int useCase, int fgPriority, int bgPriority) { diff --git a/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java b/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java new file mode 100644 index 000000000000..fdbe4b425d39 --- /dev/null +++ b/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java @@ -0,0 +1,183 @@ +/* + * 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.utils.quota; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +/** + * Can be used to rate limit events per app based on multiple rates at the same time. For example, + * it can limit an event to happen only: + * + * <li>5 times in 20 seconds</li> + * and + * <li>6 times in 40 seconds</li> + * and + * <li>10 times in 1 hour</li> + * + * <p><br> + * All listed rates apply at the same time, and the UPTC will be out of quota if it doesn't satisfy + * all the given rates. The underlying mechanism used is + * {@link com.android.server.utils.quota.CountQuotaTracker}, so all its conditions apply, as well + * as an additional constraint: all the user-package-tag combinations (UPTC) are considered to be in + * the same {@link com.android.server.utils.quota.Category}. + * </p> + * + * @hide + */ +public class MultiRateLimiter { + + private static final CountQuotaTracker[] EMPTY_TRACKER_ARRAY = {}; + + private final Object mLock = new Object(); + @GuardedBy("mLock") + private final CountQuotaTracker[] mQuotaTrackers; + + private MultiRateLimiter(List<CountQuotaTracker> quotaTrackers) { + mQuotaTrackers = quotaTrackers.toArray(EMPTY_TRACKER_ARRAY); + } + + /** Record that an event happened and count it towards the given quota. */ + public void noteEvent(int userId, @NonNull String packageName, @Nullable String tag) { + synchronized (mLock) { + noteEventLocked(userId, packageName, tag); + } + } + + /** Check whether the given UPTC is allowed to trigger an event. */ + public boolean isWithinQuota(int userId, @NonNull String packageName, @Nullable String tag) { + synchronized (mLock) { + return isWithinQuotaLocked(userId, packageName, tag); + } + } + + @GuardedBy("mLock") + private void noteEventLocked(int userId, @NonNull String packageName, @Nullable String tag) { + for (CountQuotaTracker quotaTracker : mQuotaTrackers) { + quotaTracker.noteEvent(userId, packageName, tag); + } + } + + @GuardedBy("mLock") + private boolean isWithinQuotaLocked(int userId, @NonNull String packageName, + @Nullable String tag) { + for (CountQuotaTracker quotaTracker : mQuotaTrackers) { + if (!quotaTracker.isWithinQuota(userId, packageName, tag)) { + return false; + } + } + return true; + } + + /** Can create a new {@link MultiRateLimiter}. */ + public static class Builder { + + private final List<CountQuotaTracker> mQuotaTrackers; + private final Context mContext; + private final Categorizer mCategorizer; + private final Category mCategory; + @Nullable private final QuotaTracker.Injector mInjector; + + /** + * Creates a new builder and allows to inject an object that can be used + * to manipulate elapsed time in tests. + */ + @VisibleForTesting + Builder(Context context, QuotaTracker.Injector injector) { + this.mQuotaTrackers = new ArrayList<>(); + this.mContext = context; + this.mInjector = injector; + this.mCategorizer = Categorizer.SINGLE_CATEGORIZER; + this.mCategory = Category.SINGLE_CATEGORY; + } + + /** Creates a new builder for {@link MultiRateLimiter}. */ + public Builder(Context context) { + this(context, null); + } + + /** + * Adds another rate limit to be used in {@link MultiRateLimiter}. + * + * @param limit The maximum event count an app can have in the rolling time window. + * @param windowSize The rolling time window to use when checking quota usage. + */ + public Builder addRateLimit(int limit, Duration windowSize) { + CountQuotaTracker countQuotaTracker; + if (mInjector != null) { + countQuotaTracker = new CountQuotaTracker(mContext, mCategorizer, mInjector); + } else { + countQuotaTracker = new CountQuotaTracker(mContext, mCategorizer); + } + countQuotaTracker.setCountLimit(mCategory, limit, windowSize.toMillis()); + mQuotaTrackers.add(countQuotaTracker); + return this; + } + + /** Adds another rate limit to be used in {@link MultiRateLimiter}. */ + public Builder addRateLimit(@NonNull RateLimit rateLimit) { + return addRateLimit(rateLimit.mLimit, rateLimit.mWindowSize); + } + + /** Adds all given rate limits that will be used in {@link MultiRateLimiter}. */ + public Builder addRateLimits(@NonNull RateLimit[] rateLimits) { + for (RateLimit rateLimit : rateLimits) { + addRateLimit(rateLimit); + } + return this; + } + + /** + * Return a new {@link com.android.server.utils.quota.MultiRateLimiter} using set rate + * limit. + */ + public MultiRateLimiter build() { + return new MultiRateLimiter(mQuotaTrackers); + } + } + + /** Helper class that describes a rate limit. */ + public static class RateLimit { + public final int mLimit; + public final Duration mWindowSize; + + /** + * @param limit The maximum count of some occurrence in the rolling time window. + * @param windowSize The rolling time window to use when checking quota usage. + */ + private RateLimit(int limit, Duration windowSize) { + this.mLimit = limit; + this.mWindowSize = windowSize; + } + + /** + * @param limit The maximum count of some occurrence in the rolling time window. + * @param windowSize The rolling time window to use when checking quota usage. + */ + public static RateLimit create(int limit, Duration windowSize) { + return new RateLimit(limit, windowSize); + } + } +} diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index c3d5874de609..31984531d31f 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2984,7 +2984,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } if (wallpaper.allowBackup) { - out.attribute(null, "backup", "true"); + out.attributeBoolean(null, "backup", true); } out.endTag(null, tag); @@ -3249,7 +3249,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints); } wallpaper.name = parser.getAttributeValue(null, "name"); - wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup")); + wallpaper.allowBackup = parser.getAttributeBoolean(null, "backup", false); } // Called by SystemBackupAgent after files are restored to disk. diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java new file mode 100644 index 000000000000..9f1152c8e371 --- /dev/null +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -0,0 +1,1046 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; +import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH; +import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller; +import static com.android.server.wm.Task.ActivityState.DESTROYED; +import static com.android.server.wm.Task.ActivityState.DESTROYING; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.ActivityTaskManager; +import android.app.IActivityClientController; +import android.app.PictureInPictureParams; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.PersistableBundle; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.Trace; +import android.service.voice.VoiceInteractionManagerInternal; +import android.util.Slog; +import android.view.RemoteAnimationDefinition; + +import com.android.internal.app.AssistUtils; +import com.android.internal.protolog.common.ProtoLog; +import com.android.server.LocalServices; +import com.android.server.Watchdog; +import com.android.server.uri.NeededUriGrants; +import com.android.server.vr.VrManagerInternal; + +import java.util.Arrays; + +/** + * Server side implementation for the client activity to interact with system. + * + * @see android.app.ActivityClient + */ +class ActivityClientController extends IActivityClientController.Stub { + private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityClientController" : TAG_ATM; + private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY; + + private final ActivityTaskManagerService mService; + private final WindowManagerGlobalLock mGlobalLock; + private final ActivityTaskSupervisor mTaskSupervisor; + private final Context mContext; + + /** Wrapper around VoiceInteractionServiceManager. */ + private AssistUtils mAssistUtils; + + ActivityClientController(ActivityTaskManagerService service) { + mService = service; + mGlobalLock = service.mGlobalLock; + mTaskSupervisor = service.mTaskSupervisor; + mContext = service.mContext; + } + + void onSystemReady() { + mAssistUtils = new AssistUtils(mContext); + } + + @Override + public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle"); + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r == null) { + return; + } + mTaskSupervisor.activityIdleInternal(r, false /* fromTimeout */, + false /* processPausingActivities */, config); + if (stopProfiling && r.hasProcess()) { + r.app.clearProfilerIfNeeded(); + } + } + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void activityResumed(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + synchronized (mGlobalLock) { + ActivityRecord.activityResumedLocked(token); + } + Binder.restoreCallingIdentity(origId); + } + + @Override + public void activityTopResumedStateLost() { + final long origId = Binder.clearCallingIdentity(); + synchronized (mGlobalLock) { + mTaskSupervisor.handleTopResumedStateReleased(false /* timeout */); + } + Binder.restoreCallingIdentity(origId); + } + + @Override + public void activityPaused(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + synchronized (mGlobalLock) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused"); + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r != null) { + r.activityPaused(false); + } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + Binder.restoreCallingIdentity(origId); + } + + @Override + public void activityStopped(IBinder token, Bundle icicle, PersistableBundle persistentState, + CharSequence description) { + if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token); + + // Refuse possible leaked file descriptors. + if (icicle != null && icicle.hasFileDescriptors()) { + throw new IllegalArgumentException("File descriptors passed in Bundle"); + } + + final long origId = Binder.clearCallingIdentity(); + + String restartingName = null; + int restartingUid = 0; + final ActivityRecord r; + synchronized (mGlobalLock) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped"); + r = ActivityRecord.isInStackLocked(token); + if (r != null) { + if (r.attachedToProcess() && r.isState(Task.ActivityState.RESTARTING_PROCESS)) { + // The activity was requested to restart from + // {@link #restartActivityProcessIfVisible}. + restartingName = r.app.mName; + restartingUid = r.app.mUid; + } + r.activityStopped(icicle, persistentState, description); + } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + + if (restartingName != null) { + // In order to let the foreground activity can be restarted with its saved state from + // {@link android.app.Activity#onSaveInstanceState}, the kill operation is postponed + // until the activity reports stopped with the state. And the activity record will be + // kept because the record state is restarting, then the activity will be restarted + // immediately if it is still the top one. + mTaskSupervisor.removeRestartTimeouts(r); + mService.mAmInternal.killProcess(restartingName, restartingUid, + "restartActivityProcess"); + } + mService.mAmInternal.trimApplications(); + + Binder.restoreCallingIdentity(origId); + } + + @Override + public void activityDestroyed(IBinder token) { + if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token); + final long origId = Binder.clearCallingIdentity(); + synchronized (mGlobalLock) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed"); + try { + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r != null) { + r.destroyed("activityDestroyed"); + } + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override + public void activityRelaunched(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + synchronized (mGlobalLock) { + mTaskSupervisor.activityRelaunchedLocked(token); + } + Binder.restoreCallingIdentity(origId); + } + + @Override + public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration, + int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) { + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s %s", + token, Arrays.toString(horizontalSizeConfiguration), + Arrays.toString(verticalSizeConfigurations)); + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.setSizeConfigurations(horizontalSizeConfiguration, verticalSizeConfigurations, + smallestSizeConfigurations); + } + } + } + + /** + * Attempts to move a task backwards in z-order (the order of activities within the task is + * unchanged). + * + * There are several possible results of this call: + * - if the task is locked, then we will show the lock toast. + * - if there is a task behind the provided task, then that task is made visible and resumed as + * this task is moved to the back. + * - otherwise, if there are no other tasks in the root task: + * - if this task is in the pinned mode, then we remove the task completely, which will + * have the effect of moving the task to the top or bottom of the fullscreen root task + * (depending on whether it is visible). + * - otherwise, we simply return home and hide this task. + * + * @param token A reference to the activity we wish to move. + * @param nonRoot If false then this only works if the activity is the root + * of a task; if true it will work for any activity in a task. + * @return Returns true if the move completed, false if not. + */ + @Override + public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) { + enforceNotIsolatedCaller("moveActivityTaskToBack"); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot); + final Task task = mService.mRootWindowContainer.anyTaskForId(taskId); + if (task != null) { + return ActivityRecord.getStackLocked(token).moveTaskToBack(task); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + return false; + } + + @Override + public boolean shouldUpRecreateTask(IBinder token, String destAffinity) { + synchronized (mGlobalLock) { + final ActivityRecord srec = ActivityRecord.forTokenLocked(token); + if (srec != null) { + return srec.getRootTask().shouldUpRecreateTaskLocked(srec, destAffinity); + } + } + return false; + } + + @Override + public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode, + Intent resultData) { + final ActivityRecord r; + synchronized (mGlobalLock) { + r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return false; + } + } + + // Carefully collect grants without holding lock. + final NeededUriGrants destGrants = mService.collectGrants(destIntent, r); + final NeededUriGrants resultGrants = mService.collectGrants(resultData, r.resultTo); + + synchronized (mGlobalLock) { + return r.getRootTask().navigateUpTo( + r, destIntent, destGrants, resultCode, resultData, resultGrants); + } + } + + @Override + public boolean releaseActivityInstance(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null || !r.isDestroyable()) { + return false; + } + r.destroyImmediately("app-req"); + return r.isState(DESTROYING, DESTROYED); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** + * This is the internal entry point for handling Activity.finish(). + * + * @param token The Binder token referencing the Activity we want to finish. + * @param resultCode Result code, if any, from this Activity. + * @param resultData Result data (Intent), if any, from this Activity. + * @param finishTask Whether to finish the task associated with this Activity. + * @return Returns true if the activity successfully finished, or false if it is still running. + */ + @Override + public boolean finishActivity(IBinder token, int resultCode, Intent resultData, + int finishTask) { + // Refuse possible leaked file descriptors. + if (resultData != null && resultData.hasFileDescriptors()) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + + final ActivityRecord r; + synchronized (mGlobalLock) { + r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return true; + } + } + + // Carefully collect grants without holding lock. + final NeededUriGrants resultGrants = mService.collectGrants(resultData, r.resultTo); + + synchronized (mGlobalLock) { + // Check again in case activity was removed when collecting grants. + if (!r.isInHistory()) { + return true; + } + + // Keep track of the root activity of the task before we finish it. + final Task tr = r.getTask(); + final ActivityRecord rootR = tr.getRootActivity(); + if (rootR == null) { + Slog.w(TAG, "Finishing task with all activities already finished"); + } + // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can + // finish. + if (mService.getLockTaskController().activityBlockedFromFinish(r)) { + return false; + } + + // TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked + // We should consolidate. + if (mService.mController != null) { + // Find the first activity that is not finishing. + final ActivityRecord next = + r.getRootTask().topRunningActivity(token, INVALID_TASK_ID); + if (next != null) { + // ask watcher if this is allowed + boolean resumeOK = true; + try { + resumeOK = mService.mController.activityResuming(next.packageName); + } catch (RemoteException e) { + mService.mController = null; + Watchdog.getInstance().setActivityController(null); + } + + if (!resumeOK) { + Slog.i(TAG, "Not finishing activity because controller resumed"); + return false; + } + } + } + + // Note down that the process has finished an activity and is in background activity + // starts grace period. + if (r.app != null) { + r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis()); + } + + final long origId = Binder.clearCallingIdentity(); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity"); + try { + final boolean res; + final boolean finishWithRootActivity = + finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY; + if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY + || (finishWithRootActivity && r == rootR)) { + // If requested, remove the task that is associated to this activity only if it + // was the root activity in the task. The result code and data is ignored + // because we don't support returning them across task boundaries. Also, to + // keep backwards compatibility we remove the task from recents when finishing + // task with root activity. + mTaskSupervisor.removeTask(tr, false /*killProcess*/, + finishWithRootActivity, "finish-activity"); + res = true; + // Explicitly dismissing the activity so reset its relaunch flag. + r.mRelaunchReason = RELAUNCH_REASON_NONE; + } else { + r.finishIfPossible(resultCode, resultData, resultGrants, + "app-request", true /* oomAdj */); + res = r.finishing; + if (!res) { + Slog.i(TAG, "Failed to finish by app-request"); + } + } + return res; + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override + public boolean finishActivityAffinity(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return false; + } + + // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps + // can finish. + if (mService.getLockTaskController().activityBlockedFromFinish(r)) { + return false; + } + + r.getTask().forAllActivities(activity -> r.finishIfSameAffinity(activity), + r /* boundary */, true /* includeBoundary */, + true /* traverseTopToBottom */); + return true; + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void finishSubActivity(IBinder token, String resultWho, int requestCode) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) return; + + // TODO: This should probably only loop over the task since you need to be in the + // same task to return results. + r.getRootTask().forAllActivities(activity -> { + activity.finishIfSubActivity(r /* parent */, resultWho, requestCode); + }, true /* traverseTopToBottom */); + + mService.updateOomAdj(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public boolean isTopOfTask(IBinder token) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + return r != null && r.getTask().getTopNonFinishingActivity() == r; + } + } + + @Override + public boolean willActivityBeVisible(IBinder token) { + synchronized (mGlobalLock) { + final Task rootTask = ActivityRecord.getStackLocked(token); + return rootTask != null && rootTask.willActivityBeVisible(token); + } + } + + @Override + public int getDisplayId(IBinder activityToken) { + synchronized (mGlobalLock) { + final Task rootTask = ActivityRecord.getStackLocked(activityToken); + if (rootTask != null) { + final int displayId = rootTask.getDisplayId(); + return displayId != INVALID_DISPLAY ? displayId : DEFAULT_DISPLAY; + } + return DEFAULT_DISPLAY; + } + } + + @Override + public int getTaskForActivity(IBinder token, boolean onlyRoot) { + synchronized (mGlobalLock) { + return ActivityRecord.getTaskForActivityLocked(token, onlyRoot); + } + } + + @Override + public ComponentName getCallingActivity(IBinder token) { + synchronized (mGlobalLock) { + final ActivityRecord r = getCallingRecord(token); + return r != null ? r.intent.getComponent() : null; + } + } + + @Override + public String getCallingPackage(IBinder token) { + synchronized (mGlobalLock) { + final ActivityRecord r = getCallingRecord(token); + return r != null ? r.info.packageName : null; + } + } + + private static ActivityRecord getCallingRecord(IBinder token) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + return r != null ? r.resultTo : null; + } + + @Override + public Bundle getActivityOptions(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return null; + } + final ActivityOptions activityOptions = r.takeOptionsLocked(true /* fromClient */); + return activityOptions != null ? activityOptions.toBundle() : null; + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void setRequestedOrientation(IBinder token, int requestedOrientation) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.setRequestedOrientation(requestedOrientation); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public int getRequestedOrientation(IBinder token) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + return r != null + ? r.getRequestedOrientation() : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + } + + @Override + public boolean convertFromTranslucent(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + return r != null && r.setOccludesParent(true); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public boolean convertToTranslucent(IBinder token, Bundle options) { + final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return false; + } + final ActivityRecord under = r.getTask().getActivityBelow(r); + if (under != null) { + under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null; + } + return r.setOccludesParent(false); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public boolean isImmersive(IBinder token) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + throw new IllegalArgumentException(); + } + return r.immersive; + } + } + + @Override + public void setImmersive(IBinder token, boolean immersive) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + throw new IllegalArgumentException(); + } + r.immersive = immersive; + + // Update associated state if we're frontmost. + if (r.isFocusedActivityOnDisplay()) { + ProtoLog.d(WM_DEBUG_IMMERSIVE, "Frontmost changed immersion: %s", r); + mService.applyUpdateLockStateLocked(r); + } + } + } + + @Override + public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ensureValidPictureInPictureActivityParams( + "enterPictureInPictureMode", token, params); + return mService.enterPictureInPictureMode(r, params); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ensureValidPictureInPictureActivityParams( + "setPictureInPictureParams", token, params); + + // Only update the saved args from the args that are set. + r.setPictureInPictureParams(params); + if (r.inPinnedWindowingMode()) { + // If the activity is already in picture-in-picture, update the pinned task now + // if it is not already expanding to fullscreen. Otherwise, the arguments will + // be used the next time the activity enters PiP. + final Task rootTask = r.getRootTask(); + rootTask.setPictureInPictureAspectRatio( + r.pictureInPictureArgs.getAspectRatio()); + rootTask.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** + * Checks the state of the system and the activity associated with the given {@param token} to + * verify that picture-in-picture is supported for that activity. + * + * @return the activity record for the given {@param token} if all the checks pass. + */ + private ActivityRecord ensureValidPictureInPictureActivityParams(String caller, + IBinder token, PictureInPictureParams params) { + if (!mService.mSupportsPictureInPicture) { + throw new IllegalStateException(caller + + ": Device doesn't support picture-in-picture mode."); + } + + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r == null) { + throw new IllegalStateException(caller + + ": Can't find activity for token=" + token); + } + + if (!r.supportsPictureInPicture()) { + throw new IllegalStateException(caller + + ": Current activity does not support picture-in-picture."); + } + + if (params.hasSetAspectRatio() + && !mService.mWindowManager.isValidPictureInPictureAspectRatio( + r.mDisplayContent, params.getAspectRatio())) { + final float minAspectRatio = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); + final float maxAspectRatio = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); + throw new IllegalArgumentException(String.format(caller + + ": Aspect ratio is too extreme (must be between %f and %f).", + minAspectRatio, maxAspectRatio)); + } + + // Truncate the number of actions if necessary. + params.truncateActions(ActivityTaskManager.getMaxNumPictureInPictureActions(mContext)); + return r; + } + + @Override + public void toggleFreeformWindowingMode(IBinder token) { + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r == null) { + throw new IllegalArgumentException( + "toggleFreeformWindowingMode: No activity record matching token=" + + token); + } + + final Task rootTask = r.getRootTask(); + if (rootTask == null) { + throw new IllegalStateException("toggleFreeformWindowingMode: the activity " + + "doesn't have a root task"); + } + + if (!rootTask.inFreeformWindowingMode() + && rootTask.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { + throw new IllegalStateException("toggleFreeformWindowingMode: You can only " + + "toggle between fullscreen and freeform."); + } + + if (rootTask.inFreeformWindowingMode()) { + rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + } else if (!mService.mSizeCompatFreeform && r.inSizeCompatMode()) { + throw new IllegalStateException("Size-compat windows are currently not" + + "freeform-enabled"); + } else if (rootTask.getParent().inFreeformWindowingMode()) { + // If the window is on a freeform display, set it to undefined. It will be + // resolved to freeform and it can adjust windowing mode when the display mode + // changes in runtime. + rootTask.setWindowingMode(WINDOWING_MODE_UNDEFINED); + } else { + rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void startLockTaskModeByToken(IBinder token) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r != null) { + mService.startLockTaskMode(r.getTask(), false /* isSystemCaller */); + } + } + } + + @Override + public void stopLockTaskModeByToken(IBinder token) { + mService.stopLockTaskModeInternal(token, false /* isSystemCaller */); + } + + @Override + public void showLockTaskEscapeMessage(IBinder token) { + synchronized (mGlobalLock) { + if (ActivityRecord.forTokenLocked(token) != null) { + mService.getLockTaskController().showLockTaskToast(); + } + } + } + + @Override + public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.setTaskDescription(td); + } + } + } + + @Override + public boolean showAssistFromActivity(IBinder token, Bundle args) { + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord caller = ActivityRecord.forTokenLocked(token); + final Task topRootTask = mService.getTopDisplayFocusedRootTask(); + final ActivityRecord top = topRootTask != null + ? topRootTask.getTopNonFinishingActivity() : null; + if (top != caller) { + Slog.w(TAG, "showAssistFromActivity failed: caller " + caller + + " is not current top " + top); + return false; + } + if (!top.nowVisible) { + Slog.w(TAG, "showAssistFromActivity failed: caller " + caller + + " is not visible"); + return false; + } + } + return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, + null /* showCallback */, token); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public boolean isRootVoiceInteraction(IBinder token) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + return r != null && r.rootVoiceInteraction; + } + } + + @Override + public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) { + Slog.i(TAG, "Activity tried to startLocalVoiceInteraction"); + synchronized (mGlobalLock) { + final Task topRootTask = mService.getTopDisplayFocusedRootTask(); + final ActivityRecord activity = topRootTask != null + ? topRootTask.getTopNonFinishingActivity() : null; + if (ActivityRecord.forTokenLocked(callingActivity) != activity) { + throw new SecurityException("Only focused activity can call startVoiceInteraction"); + } + if (mService.mRunningVoice != null || activity.getTask().voiceSession != null + || activity.voiceSession != null) { + Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction"); + return; + } + if (activity.pendingVoiceInteractionStart) { + Slog.w(TAG, "Pending start of voice interaction already."); + return; + } + activity.pendingVoiceInteractionStart = true; + } + LocalServices.getService(VoiceInteractionManagerInternal.class) + .startLocalVoiceInteraction(callingActivity, options); + } + + @Override + public void stopLocalVoiceInteraction(IBinder callingActivity) { + LocalServices.getService(VoiceInteractionManagerInternal.class) + .stopLocalVoiceInteraction(callingActivity); + } + + @Override + public void setShowWhenLocked(IBinder token, boolean showWhenLocked) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.setShowWhenLocked(showWhenLocked); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void setInheritShowWhenLocked(IBinder token, boolean inheritShowWhenLocked) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.setInheritShowWhenLocked(inheritShowWhenLocked); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void setTurnScreenOn(IBinder token, boolean turnScreenOn) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.setTurnScreenOn(turnScreenOn); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.reportFullyDrawnLocked(restoredFromBundle); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void overridePendingTransition(IBinder token, String packageName, + int enterAnim, int exitAnim) { + final long origId = Binder.clearCallingIdentity(); + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) { + r.mDisplayContent.mAppTransition.overridePendingAppTransition( + packageName, enterAnim, exitAnim, null, null); + } + } + Binder.restoreCallingIdentity(origId); + } + + @Override + public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) { + mService.enforceSystemHasVrFeature(); + + final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); + final ActivityRecord r; + synchronized (mGlobalLock) { + r = ActivityRecord.isInStackLocked(token); + } + if (r == null) { + throw new IllegalArgumentException(); + } + + final int err; + if ((err = vrService.hasVrPackage(packageName, r.mUserId)) != VrManagerInternal.NO_ERROR) { + return err; + } + + // Clear the binder calling uid since this path may call moveToTask(). + final long callingId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + r.requestedVrComponent = (enabled) ? packageName : null; + + // Update associated state if this activity is currently focused. + if (r.isFocusedActivityOnDisplay()) { + mService.applyUpdateVrModeLocked(r); + } + return 0; + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + @Override + public void setDisablePreviewScreenshots(IBinder token, boolean disable) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.setDisablePreviewScreenshots(disable); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) { + mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, + "registerRemoteAnimations"); + definition.setCallingPidUid(Binder.getCallingPid(), Binder.getCallingUid()); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.registerRemoteAnimations(definition); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void unregisterRemoteAnimations(IBinder token) { + mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, + "unregisterRemoteAnimations"); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.unregisterRemoteAnimations(); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void onBackPressedOnTaskRoot(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return; + } + if (mService.mWindowOrganizerController.mTaskOrganizerController + .handleInterceptBackPressedOnTaskRoot(r.getRootTask())) { + // This task is handled by a task organizer that has requested the back pressed + // callback. + } else { + moveActivityTaskToBack(token, false /* nonRoot */); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } +} diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1b8cc082f598..743796b0aad0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -925,8 +925,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mVoiceInteraction) { pw.println(prefix + "mVoiceInteraction=true"); } - pw.print(prefix); pw.print("mOccludesParent="); pw.print(mOccludesParent); - pw.print(" mOrientation="); pw.println(mOrientation); + pw.print(prefix); pw.print("mOccludesParent="); pw.println(mOccludesParent); + pw.print(prefix); pw.print("mOrientation="); + pw.println(ActivityInfo.screenOrientationToString(mOrientation)); pw.println(prefix + "mVisibleRequested=" + mVisibleRequested + " mVisible=" + mVisible + " mClientVisible=" + mClientVisible + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "") @@ -1011,6 +1012,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (info.supportsSizeChanges) { pw.println(prefix + "supportsSizeChanges=true"); } + if (info.configChanges != 0) { + pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges)); + } } } @@ -4032,7 +4036,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mDisplayContent != null) { mDisplayContent.setLayoutNeeded(); } - mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, token).sendToTarget(); + mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, this).sendToTarget(); } } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 3710120a7934..8298dfd85114 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -33,8 +33,6 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -62,7 +60,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIV import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL; import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS; import static android.provider.Settings.System.FONT_SCALE; -import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -95,15 +92,8 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Scr import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen; import static com.android.server.am.EventLogTags.writeConfigurationChanged; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IMMERSIVE; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; @@ -121,8 +111,6 @@ import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_P import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_ONLY; import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS; -import static com.android.server.wm.Task.ActivityState.DESTROYED; -import static com.android.server.wm.Task.ActivityState.DESTROYING; import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; @@ -132,7 +120,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; @@ -143,6 +130,7 @@ import android.app.AlertDialog; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.Dialog; +import android.app.IActivityClientController; import android.app.IActivityController; import android.app.IActivityTaskManager; import android.app.IApplicationThread; @@ -197,7 +185,6 @@ import android.os.IUserManager; import android.os.LocaleList; import android.os.Looper; import android.os.Message; -import android.os.PersistableBundle; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.Process; @@ -237,7 +224,6 @@ import android.window.WindowContainerTransaction; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.AssistUtils; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ProcessMap; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; @@ -249,8 +235,6 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; -import com.android.internal.util.function.pooled.PooledConsumer; -import com.android.internal.util.function.pooled.PooledFunction; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.AttributeCache; import com.android.server.LocalServices; @@ -271,7 +255,6 @@ import com.android.server.pm.UserManagerService; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.uri.NeededUriGrants; import com.android.server.uri.UriGrantsManagerInternal; -import com.android.server.vr.VrManagerInternal; import java.io.BufferedReader; import java.io.File; @@ -308,11 +291,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM; static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK; static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; - private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE; - private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS; - private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY; - private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK; - private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; // How long we wait until we timeout on key dispatching during instrumentation. static final long INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS = 60 * 1000; @@ -385,6 +363,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { */ final Object mGlobalLockWithoutBoost = mGlobalLock; ActivityTaskSupervisor mTaskSupervisor; + ActivityClientController mActivityClientController; RootWindowContainer mRootWindowContainer; WindowManagerService mWindowManager; private UserManagerService mUserManager; @@ -418,9 +397,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** State of external calls telling us if the device is awake or asleep. */ private boolean mKeyguardShown = false; - // Wrapper around VoiceInteractionServiceManager - private AssistUtils mAssistUtils; - // VoiceInteraction session ID that changes for each new request except when // being called for multi-window assist in a single session. private int mViSessionId = 1000; @@ -763,10 +739,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final PackageManager pm = mContext.getPackageManager(); mHasHeavyWeightFeature = pm.hasSystemFeature(FEATURE_CANT_SAVE_STATE); mHasLeanbackFeature = pm.hasSystemFeature(FEATURE_LEANBACK); - mAssistUtils = new AssistUtils(mContext); mVrController.onSystemReady(); mRecentTasks.onSystemReadyLocked(); mTaskSupervisor.onSystemReady(); + mActivityClientController.onSystemReady(); mBlockActivityAfterHomeEnabled = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, BLOCK_ACTIVITY_STARTS_AFTER_HOME_FLAG, false); @@ -872,6 +848,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mCompatModePackages = new CompatModePackages(this, systemDir, mH); mPendingIntentController = intentController; mTaskSupervisor = createTaskSupervisor(); + mActivityClientController = new ActivityClientController(this); mTaskChangeNotificationController = new TaskChangeNotificationController(mGlobalLock, mTaskSupervisor, mH); @@ -1698,311 +1675,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mAmInternal.getActivityInfoForUser(aInfo, userId); } - /** - * This is the internal entry point for handling Activity.finish(). - * - * @param token The Binder token referencing the Activity we want to finish. - * @param resultCode Result code, if any, from this Activity. - * @param resultData Result data (Intent), if any, from this Activity. - * @param finishTask Whether to finish the task associated with this Activity. - * @return Returns true if the activity successfully finished, or false if it is still running. - */ - @Override - public final boolean finishActivity(IBinder token, int resultCode, Intent resultData, - int finishTask) { - // Refuse possible leaked file descriptors - if (resultData != null && resultData.hasFileDescriptors()) { - throw new IllegalArgumentException("File descriptors passed in Intent"); - } - - final ActivityRecord r; - synchronized (mGlobalLock) { - r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return true; - } - } - - // Carefully collect grants without holding lock - final NeededUriGrants resultGrants = collectGrants(resultData, r.resultTo); - - synchronized (mGlobalLock) { - // Sanity check in case activity was removed before entering global lock. - if (!r.isInHistory()) { - return true; - } - - // Keep track of the root activity of the task before we finish it - final Task tr = r.getTask(); - final ActivityRecord rootR = tr.getRootActivity(); - if (rootR == null) { - Slog.w(TAG, "Finishing task with all activities already finished"); - } - // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can - // finish. - if (getLockTaskController().activityBlockedFromFinish(r)) { - return false; - } - - // TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked - // We should consolidate. - if (mController != null) { - // Find the first activity that is not finishing. - final ActivityRecord next = - r.getRootTask().topRunningActivity(token, INVALID_TASK_ID); - if (next != null) { - // ask watcher if this is allowed - boolean resumeOK = true; - try { - resumeOK = mController.activityResuming(next.packageName); - } catch (RemoteException e) { - mController = null; - Watchdog.getInstance().setActivityController(null); - } - - if (!resumeOK) { - Slog.i(TAG, "Not finishing activity because controller resumed"); - return false; - } - } - } - - // note down that the process has finished an activity and is in background activity - // starts grace period - if (r.app != null) { - r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis()); - } - - final long origId = Binder.clearCallingIdentity(); - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity"); - try { - boolean res; - final boolean finishWithRootActivity = - finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY; - if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY - || (finishWithRootActivity && r == rootR)) { - // If requested, remove the task that is associated to this activity only if it - // was the root activity in the task. The result code and data is ignored - // because we don't support returning them across task boundaries. Also, to - // keep backwards compatibility we remove the task from recents when finishing - // task with root activity. - mTaskSupervisor.removeTask(tr, false /*killProcess*/, - finishWithRootActivity, "finish-activity"); - res = true; - // Explicitly dismissing the activity so reset its relaunch flag. - r.mRelaunchReason = RELAUNCH_REASON_NONE; - } else { - r.finishIfPossible(resultCode, resultData, resultGrants, - "app-request", true /* oomAdj */); - res = r.finishing; - if (!res) { - Slog.i(TAG, "Failed to finish by app-request"); - } - } - return res; - } finally { - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public boolean finishActivityAffinity(IBinder token) { - synchronized (mGlobalLock) { - final long origId = Binder.clearCallingIdentity(); - try { - ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return false; - } - - // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps - // can finish. - if (getLockTaskController().activityBlockedFromFinish(r)) { - return false; - } - - final PooledFunction p = PooledLambda.obtainFunction( - ActivityRecord::finishIfSameAffinity, r, - PooledLambda.__(ActivityRecord.class)); - r.getTask().forAllActivities( - p, r, true /*includeBoundary*/, true /*traverseTopToBottom*/); - p.recycle(); - - return true; - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) { - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle"); - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r == null) { - return; - } - mTaskSupervisor.activityIdleInternal(r, false /* fromTimeout */, - false /* processPausingActivities */, config); - if (stopProfiling && r.hasProcess()) { - r.app.clearProfilerIfNeeded(); - } - } - } finally { - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - Binder.restoreCallingIdentity(origId); - } - } - - @Override - public final void activityResumed(IBinder token) { - final long origId = Binder.clearCallingIdentity(); - synchronized (mGlobalLock) { - ActivityRecord.activityResumedLocked(token); - } - Binder.restoreCallingIdentity(origId); - } - - @Override - public final void activityTopResumedStateLost() { - final long origId = Binder.clearCallingIdentity(); - synchronized (mGlobalLock) { - mTaskSupervisor.handleTopResumedStateReleased(false /* timeout */); - } - Binder.restoreCallingIdentity(origId); - } - - @Override - public final void activityPaused(IBinder token) { - final long origId = Binder.clearCallingIdentity(); - synchronized (mGlobalLock) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused"); - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r != null) { - r.activityPaused(false); - } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } - Binder.restoreCallingIdentity(origId); - } - - @Override - public final void activityStopped(IBinder token, Bundle icicle, - PersistableBundle persistentState, CharSequence description) { - if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token); - - // Refuse possible leaked file descriptors - if (icicle != null && icicle.hasFileDescriptors()) { - throw new IllegalArgumentException("File descriptors passed in Bundle"); - } - - final long origId = Binder.clearCallingIdentity(); - - String restartingName = null; - int restartingUid = 0; - final ActivityRecord r; - synchronized (mGlobalLock) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped"); - r = ActivityRecord.isInStackLocked(token); - if (r != null) { - if (r.attachedToProcess() - && r.isState(Task.ActivityState.RESTARTING_PROCESS)) { - // The activity was requested to restart from - // {@link #restartActivityProcessIfVisible}. - restartingName = r.app.mName; - restartingUid = r.app.mUid; - } - r.activityStopped(icicle, persistentState, description); - } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } - - if (restartingName != null) { - // In order to let the foreground activity can be restarted with its saved state from - // {@link android.app.Activity#onSaveInstanceState}, the kill operation is postponed - // until the activity reports stopped with the state. And the activity record will be - // kept because the record state is restarting, then the activity will be restarted - // immediately if it is still the top one. - mTaskSupervisor.removeRestartTimeouts(r); - mAmInternal.killProcess(restartingName, restartingUid, "restartActivityProcess"); - } - mAmInternal.trimApplications(); - - Binder.restoreCallingIdentity(origId); - } - - @Override - public final void activityDestroyed(IBinder token) { - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token); - synchronized (mGlobalLock) { - final long origId = Binder.clearCallingIdentity(); - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed"); - try { - final ActivityRecord activity = ActivityRecord.forTokenLocked(token); - if (activity != null) { - activity.destroyed("activityDestroyed"); - } - } finally { - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public final void activityRelaunched(IBinder token) { - final long origId = Binder.clearCallingIdentity(); - synchronized (mGlobalLock) { - mTaskSupervisor.activityRelaunchedLocked(token); - } - Binder.restoreCallingIdentity(origId); - } - @Override - public void setRequestedOrientation(IBinder token, int requestedOrientation) { - synchronized (mGlobalLock) { - ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return; - } - final long origId = Binder.clearCallingIdentity(); - try { - r.setRequestedOrientation(requestedOrientation); - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public int getRequestedOrientation(IBinder token) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - return (r != null) - ? r.getRequestedOrientation() : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - } - } - - @Override - public void setImmersive(IBinder token, boolean immersive) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - throw new IllegalArgumentException(); - } - r.immersive = immersive; - - // update associated state if we're frontmost - if (r.isFocusedActivityOnDisplay()) { - ProtoLog.d(WM_DEBUG_IMMERSIVE, "Frontmost changed immersion: %s", r); - applyUpdateLockStateLocked(r); - } - } + public IActivityClientController getActivityClientController() { + return mActivityClientController; } void applyUpdateLockStateLocked(ActivityRecord r) { @@ -2025,17 +1700,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public boolean isImmersive(IBinder token) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - throw new IllegalArgumentException(); - } - return r.immersive; - } - } - - @Override public boolean isTopActivityImmersive() { enforceNotIsolatedCaller("isTopActivityImmersive"); synchronized (mGlobalLock) { @@ -2050,27 +1714,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void overridePendingTransition(IBinder token, String packageName, - int enterAnim, int exitAnim) { - synchronized (mGlobalLock) { - ActivityRecord self = ActivityRecord.isInStackLocked(token); - if (self == null) { - return; - } - - final long origId = Binder.clearCallingIdentity(); - - if (self.isState( - Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) { - self.mDisplayContent.mAppTransition.overridePendingAppTransition( - packageName, enterAnim, exitAnim, null, null); - } - - Binder.restoreCallingIdentity(origId); - } - } - - @Override public int getFrontActivityScreenCompatMode() { enforceNotIsolatedCaller("getFrontActivityScreenCompatMode"); synchronized (mGlobalLock) { @@ -2125,77 +1768,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public boolean convertFromTranslucent(IBinder token) { - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return false; - } - return r.setOccludesParent(true); - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - - @Override - public boolean convertToTranslucent(IBinder token, Bundle options) { - SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options); - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return false; - } - final ActivityRecord under = r.getTask().getActivityBelow(r); - if (under != null) { - under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null; - } - return r.setOccludesParent(false); - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - - @Override - public void notifyActivityDrawn(IBinder token) { - if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token); - synchronized (mGlobalLock) { - ActivityRecord r = mRootWindowContainer.isInAnyTask(token); - if (r != null) { - r.getRootTask().notifyActivityDrawnLocked(r); - } - } - } - - @Override - public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) { - synchronized (mGlobalLock) { - ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return; - } - r.reportFullyDrawnLocked(restoredFromBundle); - } - } - - @Override - public int getDisplayId(IBinder activityToken) throws RemoteException { - synchronized (mGlobalLock) { - final Task stack = ActivityRecord.getStackLocked(activityToken); - if (stack != null) { - final int displayId = stack.getDisplayId(); - return displayId != INVALID_DISPLAY ? displayId : DEFAULT_DISPLAY; - } - return DEFAULT_DISPLAY; - } - } - - @Override public RootTaskInfo getFocusedRootTaskInfo() throws RemoteException { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "getFocusedRootTaskInfo()"); final long ident = Binder.clearCallingIdentity(); @@ -2312,75 +1884,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public boolean shouldUpRecreateTask(IBinder token, String destAffinity) { - synchronized (mGlobalLock) { - final ActivityRecord srec = ActivityRecord.forTokenLocked(token); - if (srec != null) { - return srec.getRootTask().shouldUpRecreateTaskLocked(srec, destAffinity); - } - } - return false; - } - - @Override - public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode, - Intent resultData) { - final ActivityRecord r; - synchronized (mGlobalLock) { - r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return false; - } - } - - // Carefully collect grants without holding lock - final NeededUriGrants destGrants = collectGrants(destIntent, r); - final NeededUriGrants resultGrants = collectGrants(resultData, r.resultTo); - - synchronized (mGlobalLock) { - return r.getRootTask().navigateUpTo( - r, destIntent, destGrants, resultCode, resultData, resultGrants); - } - } - - /** - * Attempts to move a task backwards in z-order (the order of activities within the task is - * unchanged). - * - * There are several possible results of this call: - * - if the task is locked, then we will show the lock toast - * - if there is a task behind the provided task, then that task is made visible and resumed as - * this task is moved to the back - * - otherwise, if there are no other tasks in the stack: - * - if this task is in the pinned stack, then we remove the stack completely, which will - * have the effect of moving the task to the top or bottom of the fullscreen stack - * (depending on whether it is visible) - * - otherwise, we simply return home and hide this task - * - * @param token A reference to the activity we wish to move - * @param nonRoot If false then this only works if the activity is the root - * of a task; if true it will work for any activity in a task. - * @return Returns true if the move completed, false if not. - */ - @Override - public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) { - enforceNotIsolatedCaller("moveActivityTaskToBack"); - synchronized (mGlobalLock) { - final long origId = Binder.clearCallingIdentity(); - try { - int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot); - final Task task = mRootWindowContainer.anyTaskForId(taskId); - if (task != null) { - return ActivityRecord.getStackLocked(token).moveTaskToBack(task); - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - return false; - } - - @Override public Rect getTaskBounds(int taskId) { enforceTaskPermission("getTaskBounds()"); final long ident = Binder.clearCallingIdentity(); @@ -2467,31 +1970,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - public String getCallingPackage(IBinder token) { - synchronized (mGlobalLock) { - ActivityRecord r = getCallingRecordLocked(token); - return r != null ? r.info.packageName : null; - } - } - - @Override - public ComponentName getCallingActivity(IBinder token) { - synchronized (mGlobalLock) { - ActivityRecord r = getCallingRecordLocked(token); - return r != null ? r.intent.getComponent() : null; - } - } - - private ActivityRecord getCallingRecordLocked(IBinder token) { - ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return null; - } - return r.resultTo; - } - - private NeededUriGrants collectGrants(Intent intent, ActivityRecord target) { + NeededUriGrants collectGrants(Intent intent, ActivityRecord target) { if (target != null) { return mUgmInternal.checkGrantUriPermissionFromIntent(intent, Binder.getCallingUid(), target.packageName, target.mUserId); @@ -2518,25 +1997,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - public void onBackPressedOnTaskRoot(IBinder token) { - synchronized (mGlobalLock) { - ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return; - } - Task stack = r.getRootTask(); - final TaskOrganizerController taskOrgController = - mWindowOrganizerController.mTaskOrganizerController; - if (taskOrgController.handleInterceptBackPressedOnTaskRoot(stack)) { - // This task is handled by a task organizer that has requested the back pressed - // callback - } else { - moveActivityTaskToBack(token, false /* nonRoot */); - } - } - } - /** * TODO: Add mController hook */ @@ -2731,13 +2191,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - public int getTaskForActivity(IBinder token, boolean onlyRoot) { - synchronized (mGlobalLock) { - return ActivityRecord.getTaskForActivityLocked(token, onlyRoot); - } - } - List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) { return getTasks(maxNum, false /* filterForVisibleRecents */); } @@ -2772,40 +2225,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public final void finishSubActivity(IBinder token, String resultWho, int requestCode) { - synchronized (mGlobalLock) { - final long origId = Binder.clearCallingIdentity(); - try { - ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) return; - - final PooledConsumer c = PooledLambda.obtainConsumer( - ActivityRecord::finishIfSubActivity, PooledLambda.__(ActivityRecord.class), - r, resultWho, requestCode); - // TODO: This should probably only loop over the task since you need to be in the - // same task to return results. - r.getRootTask().forAllActivities(c); - c.recycle(); - - updateOomAdj(); - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public boolean willActivityBeVisible(IBinder token) { - synchronized (mGlobalLock) { - Task stack = ActivityRecord.getStackLocked(token); - if (stack != null) { - return stack.willActivityBeVisible(token); - } - return false; - } - } - - @Override public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "moveTaskToRootTask()"); synchronized (mGlobalLock) { @@ -3035,17 +2454,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void startLockTaskModeByToken(IBinder token) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r == null) { - return; - } - startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */); - } - } - - @Override public void startSystemLockTaskMode(int taskId) { enforceTaskPermission("startSystemLockTaskMode"); // This makes inner call to look as if it was initiated by system. @@ -3060,18 +2468,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // When starting lock task mode the stack must be in front and focused task.getRootTask().moveToFront("startSystemLockTaskMode"); - startLockTaskModeLocked(task, true /* isSystemCaller */); + startLockTaskMode(task, true /* isSystemCaller */); } } finally { Binder.restoreCallingIdentity(ident); } } - @Override - public void stopLockTaskModeByToken(IBinder token) { - stopLockTaskModeInternal(token, false /* isSystemCaller */); - } - /** * This API should be called by SystemUI only when user perform certain action to dismiss * lock task mode. We should only dismiss pinned lock task mode in this case. @@ -3082,8 +2485,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { stopLockTaskModeInternal(null, true /* isSystemCaller */); } - private void startLockTaskModeLocked(@Nullable Task task, boolean isSystemCaller) { - ProtoLog.w(WM_DEBUG_LOCKTASK, "startLockTaskModeLocked: %s", task); + void startLockTaskMode(@Nullable Task task, boolean isSystemCaller) { + ProtoLog.w(WM_DEBUG_LOCKTASK, "startLockTaskMode: %s", task); if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) { return; } @@ -3111,7 +2514,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - private void stopLockTaskModeInternal(@Nullable IBinder token, boolean isSystemCaller) { + void stopLockTaskModeInternal(@Nullable IBinder token, boolean isSystemCaller) { final int callingUid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { @@ -3163,34 +2566,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) { - synchronized (mGlobalLock) { - ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r != null) { - r.setTaskDescription(td); - } - } - } - - @Override - public Bundle getActivityOptions(IBinder token) { - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r != null) { - final ActivityOptions activityOptions = r.takeOptionsLocked( - true /* fromClient */); - return activityOptions == null ? null : activityOptions.toBundle(); - } - return null; - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - - @Override public List<IBinder> getAppTasks(String callingPackage) { int callingUid = Binder.getCallingUid(); assertPackageMatchesCallingUid(callingPackage); @@ -3220,39 +2595,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public boolean isTopOfTask(IBinder token) { - synchronized (mGlobalLock) { - ActivityRecord r = ActivityRecord.isInStackLocked(token); - return r != null && r.getTask().getTopNonFinishingActivity() == r; - } - } - - @Override - public void notifyLaunchTaskBehindComplete(IBinder token) { - mTaskSupervisor.scheduleLaunchTaskBehindComplete(token); - } - - @Override - public void notifyEnterAnimationComplete(IBinder token) { - mH.post(() -> { - synchronized (mGlobalLock) { - ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r != null && r.attachedToProcess()) { - try { - r.app.getThread().scheduleEnterAnimationComplete(r.appToken); - } catch (RemoteException e) { - } - } - } - - }); - } - - /** Called from an app when assist data is ready. */ - @Override - public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure, - AssistContent content, Uri referrer) { - PendingAssistExtras pae = (PendingAssistExtras) token; + public void reportAssistContextExtras(IBinder assistToken, Bundle extras, + AssistStructure structure, AssistContent content, Uri referrer) { + final PendingAssistExtras pae = (PendingAssistExtras) assistToken; synchronized (pae) { pae.result = extras; pae.structure = structure; @@ -3440,23 +2785,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public boolean releaseActivityInstance(IBinder token) { - synchronized (mGlobalLock) { - final long origId = Binder.clearCallingIdentity(); - try { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null || !r.isDestroyable()) { - return false; - } - r.destroyImmediately("app-req"); - return r.isState(DESTROYING, DESTROYED); - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override public void releaseSomeActivities(IApplicationThread appInt) { synchronized (mGlobalLock) { final long origId = Binder.clearCallingIdentity(); @@ -3540,49 +2868,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - public void toggleFreeformWindowingMode(IBinder token) { - synchronized (mGlobalLock) { - final long ident = Binder.clearCallingIdentity(); - try { - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r == null) { - throw new IllegalArgumentException( - "toggleFreeformWindowingMode: No activity record matching token=" - + token); - } - - final Task stack = r.getRootTask(); - if (stack == null) { - throw new IllegalStateException("toggleFreeformWindowingMode: the activity " - + "doesn't have a stack"); - } - - if (!stack.inFreeformWindowingMode() - && stack.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { - throw new IllegalStateException("toggleFreeformWindowingMode: You can only " - + "toggle between fullscreen and freeform."); - } - - if (stack.inFreeformWindowingMode()) { - stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - } else if (!mSizeCompatFreeform && r.inSizeCompatMode()) { - throw new IllegalStateException("Size-compat windows are currently not" - + "freeform-enabled"); - } else if (stack.getParent().inFreeformWindowingMode()) { - // If the window is on a freeform display, set it to undefined. It will be - // resolved to freeform and it can adjust windowing mode when the display mode - // changes in runtime. - stack.setWindowingMode(WINDOWING_MODE_UNDEFINED); - } else { - stack.setWindowingMode(WINDOWING_MODE_FREEFORM); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - /** Sets the task stack listener that gets callbacks when a task stack changes. */ @Override public void registerTaskStackListener(ITaskStackListener listener) { @@ -3884,42 +3169,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return DevicePolicyCache.getInstance().isScreenCaptureAllowed(userId, false); } - @Override - public boolean showAssistFromActivity(IBinder token, Bundle args) { - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - ActivityRecord caller = ActivityRecord.forTokenLocked(token); - ActivityRecord top = getTopDisplayFocusedRootTask().getTopNonFinishingActivity(); - if (top != caller) { - Slog.w(TAG, "showAssistFromActivity failed: caller " + caller - + " is not current top " + top); - return false; - } - if (!top.nowVisible) { - Slog.w(TAG, "showAssistFromActivity failed: caller " + caller - + " is not visible"); - return false; - } - } - return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, null, - token); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public boolean isRootVoiceInteraction(IBinder token) { - synchronized (mGlobalLock) { - ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return false; - } - return r.rootVoiceInteraction; - } - } - private void onLocalVoiceInteractionStartedLocked(IBinder activity, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) { ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity); @@ -4001,17 +3250,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void showLockTaskEscapeMessage(IBinder token) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r == null) { - return; - } - getLockTaskController().showLockTaskToast(); - } - } - - @Override public void keyguardGoingAway(int flags) { enforceNotIsolatedCaller("keyguardGoingAway"); final long token = Binder.clearCallingIdentity(); @@ -4025,22 +3263,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration, - int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) { - ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s %s", - token, Arrays.toString(horizontalSizeConfiguration), - Arrays.toString(verticalSizeConfigurations)); - synchronized (mGlobalLock) { - ActivityRecord record = ActivityRecord.isInStackLocked(token); - if (record == null) { - return; - } - record.setSizeConfigurations(horizontalSizeConfiguration, - verticalSizeConfigurations, smallestSizeConfigurations); - } - } - - @Override public void suppressResizeConfigChanges(boolean suppress) throws RemoteException { mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS, "suppressResizeConfigChanges()"); @@ -4138,100 +3360,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return true; } - @Override - public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) { - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked( - "enterPictureInPictureMode", token, params); - return enterPictureInPictureMode(r, params); - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - - @Override - public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) { - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked( - "setPictureInPictureParams", token, params); - - // Only update the saved args from the args that are set - r.setPictureInPictureParams(params); - if (r.inPinnedWindowingMode()) { - // If the activity is already in picture-in-picture, update the pinned stack now - // if it is not already expanding to fullscreen. Otherwise, the arguments will - // be used the next time the activity enters PiP - final Task stack = r.getRootTask(); - stack.setPictureInPictureAspectRatio( - r.pictureInPictureArgs.getAspectRatio()); - stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); - } - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - - /** - * Checks the state of the system and the activity associated with the given {@param token} to - * verify that picture-in-picture is supported for that activity. - * - * @return the activity record for the given {@param token} if all the checks pass. - */ - private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller, - IBinder token, PictureInPictureParams params) { - if (!mSupportsPictureInPicture) { - throw new IllegalStateException(caller - + ": Device doesn't support picture-in-picture mode."); - } - - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r == null) { - throw new IllegalStateException(caller - + ": Can't find activity for token=" + token); - } - - if (!r.supportsPictureInPicture()) { - throw new IllegalStateException(caller - + ": Current activity does not support picture-in-picture."); - } - - if (params.hasSetAspectRatio() - && !mWindowManager.isValidPictureInPictureAspectRatio( - r.mDisplayContent, params.getAspectRatio())) { - final float minAspectRatio = mContext.getResources().getFloat( - com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); - final float maxAspectRatio = mContext.getResources().getFloat( - com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); - throw new IllegalArgumentException(String.format(caller - + ": Aspect ratio is too extreme (must be between %f and %f).", - minAspectRatio, maxAspectRatio)); - } - - // Truncate the number of actions if necessary - params.truncateActions(ActivityTaskManager.getMaxNumPictureInPictureActions(mContext)); - - return r; - } - - @Override - public IBinder getUriPermissionOwnerForActivity(IBinder activityToken) { - enforceNotIsolatedCaller("getUriPermissionOwnerForActivity"); - synchronized (mGlobalLock) { - ActivityRecord r = ActivityRecord.isInStackLocked(activityToken); - if (r == null) { - throw new IllegalArgumentException("Activity does not exist; token=" - + activityToken); - } - return r.getUriPermissionsLocked().getExternalToken(); - } - } - // TODO(b/149338177): remove when CTS no-longer requires it @Override public void resizePrimarySplitScreen(Rect dockedBounds, Rect tempDockedTaskBounds, @@ -4304,73 +3432,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) { - enforceSystemHasVrFeature(); - - final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); - - ActivityRecord r; - synchronized (mGlobalLock) { - r = ActivityRecord.isInStackLocked(token); - } - - if (r == null) { - throw new IllegalArgumentException(); - } - - int err; - if ((err = vrService.hasVrPackage(packageName, r.mUserId)) != - VrManagerInternal.NO_ERROR) { - return err; - } - - // Clear the binder calling uid since this path may call moveToTask(). - final long callingId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - r.requestedVrComponent = (enabled) ? packageName : null; - - // Update associated state if this activity is currently focused - if (r.isFocusedActivityOnDisplay()) { - applyUpdateVrModeLocked(r); - } - return 0; - } - } finally { - Binder.restoreCallingIdentity(callingId); - } - } - - @Override - public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) { - Slog.i(TAG, "Activity tried to startLocalVoiceInteraction"); - synchronized (mGlobalLock) { - ActivityRecord activity = getTopDisplayFocusedRootTask().getTopNonFinishingActivity(); - if (ActivityRecord.forTokenLocked(callingActivity) != activity) { - throw new SecurityException("Only focused activity can call startVoiceInteraction"); - } - if (mRunningVoice != null || activity.getTask().voiceSession != null - || activity.voiceSession != null) { - Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction"); - return; - } - if (activity.pendingVoiceInteractionStart) { - Slog.w(TAG, "Pending start of voice interaction already."); - return; - } - activity.pendingVoiceInteractionStart = true; - } - LocalServices.getService(VoiceInteractionManagerInternal.class) - .startLocalVoiceInteraction(callingActivity, options); - } - - @Override - public void stopLocalVoiceInteraction(IBinder callingActivity) { - LocalServices.getService(VoiceInteractionManagerInternal.class) - .stopLocalVoiceInteraction(callingActivity); - } - - @Override public boolean supportsLocalVoiceInteraction() { return LocalServices.getService(VoiceInteractionManagerInternal.class) .supportsLocalVoiceInteraction(); @@ -4474,24 +3535,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void setDisablePreviewScreenshots(IBinder token, boolean disable) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - Slog.w(TAG, "setDisablePreviewScreenshots: Unable to find activity for token=" - + token); - return; - } - final long origId = Binder.clearCallingIdentity(); - try { - r.setDisablePreviewScreenshots(disable); - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override public void invalidateHomeTaskSnapshot(IBinder token) { synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInStackLocked(token); @@ -4532,91 +3575,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void setShowWhenLocked(IBinder token, boolean showWhenLocked) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return; - } - final long origId = Binder.clearCallingIdentity(); - try { - r.setShowWhenLocked(showWhenLocked); - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public void setInheritShowWhenLocked(IBinder token, boolean inheritShowWhenLocked) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return; - } - final long origId = Binder.clearCallingIdentity(); - try { - r.setInheritShowWhenLocked(inheritShowWhenLocked); - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public void setTurnScreenOn(IBinder token, boolean turnScreenOn) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return; - } - final long origId = Binder.clearCallingIdentity(); - try { - r.setTurnScreenOn(turnScreenOn); - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) { - mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, - "registerRemoteAnimations"); - definition.setCallingPidUid(Binder.getCallingPid(), Binder.getCallingUid()); - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return; - } - final long origId = Binder.clearCallingIdentity(); - try { - r.registerRemoteAnimations(definition); - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public void unregisterRemoteAnimations(IBinder token) { - mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, - "unregisterRemoteAnimations"); - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return; - } - final long origId = Binder.clearCallingIdentity(); - try { - r.unregisterRemoteAnimations(); - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override public void registerRemoteAnimationForNextActivityStart(String packageName, RemoteAnimationAdapter adapter) { mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, @@ -4743,7 +3701,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mVrController.shouldDisableNonVrUiLocked(); } - private void applyUpdateVrModeLocked(ActivityRecord r) { + void applyUpdateVrModeLocked(ActivityRecord r) { // VR apps are expected to run in a main display. If an app is turning on VR for // itself, but isn't on the main display, then move it there before enabling VR Mode. if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) { @@ -5097,7 +4055,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mAmInternal.getCurrentUserId(); } - private void enforceNotIsolatedCaller(String caller) { + static void enforceNotIsolatedCaller(String caller) { if (UserHandle.isIsolated(Binder.getCallingUid())) { throw new SecurityException("Isolated process not allowed to call " + caller); } diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java index 55200b566979..994f07959f3b 100644 --- a/services/core/java/com/android/server/wm/AppWarnings.java +++ b/services/core/java/com/android/server/wm/AppWarnings.java @@ -31,16 +31,12 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; -import com.android.internal.util.FastXmlSerializer; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -541,15 +537,7 @@ class AppWarnings { if ("package".equals(tagName)) { final String name = parser.getAttributeValue(null, "name"); if (name != null) { - final String flags = parser.getAttributeValue( - null, "flags"); - int flagsInt = 0; - if (flags != null) { - try { - flagsInt = Integer.parseInt(flags); - } catch (NumberFormatException e) { - } - } + int flagsInt = parser.getAttributeInt(null, "flags", 0); mPackageFlags.put(name, flagsInt); } } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java index 3d84e1752e6a..ce6b7f917991 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java @@ -23,7 +23,7 @@ import android.content.res.Configuration; */ public interface ConfigurationContainerListener { - /** {@see ConfigurationContainer#onRequestedOverrideConfigurationChanged} */ + /** @see ConfigurationContainer#onRequestedOverrideConfigurationChanged */ default void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {} /** Called when new merged override configuration is reported. */ diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index a4ac16f2ec77..15483cb90ce2 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -174,6 +174,13 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { return false; } + if (mDisplayContent.mFocusedApp != null) { + // We record the last focused TDA that respects orientation request, check if this + // change may affect it. + mDisplayContent.onLastFocusedTaskDisplayAreaChanged( + mDisplayContent.mFocusedApp.getDisplayArea()); + } + // The orientation request from this DA may now be respected. if (!ignoreOrientationRequest) { return mDisplayContent.updateOrientation(); @@ -307,6 +314,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { return this; } + /** Cheap way of doing cast and instanceof. */ + DisplayArea.Tokens asTokens() { + return null; + } + @Override void forAllDisplayAreas(Consumer<DisplayArea> callback) { super.forAllDisplayAreas(callback); @@ -544,6 +556,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { mLastOrientationSource = win; return req; } + + @Override + final DisplayArea.Tokens asTokens() { + return this; + } } @Override diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 211bd5d9eb77..d4b319a525da 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -35,6 +35,7 @@ import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; import static com.android.server.wm.DisplayAreaPolicyBuilder.HierarchyBuilder; import android.content.res.Resources; +import android.os.Bundle; import android.text.TextUtils; import java.util.ArrayList; @@ -71,6 +72,10 @@ public abstract class DisplayAreaPolicy { */ public abstract void addWindow(WindowToken token); + /** Gets the {@link DisplayArea} which a {@link WindowToken} is about to be attached to. */ + public abstract DisplayArea.Tokens getDisplayAreaForWindowToken(int type, Bundle options, + boolean ownerCanManageAppTokens, boolean roundedCornerOverlay); + /** * Gets the set of {@link DisplayArea} that are created for the given feature to apply to. */ @@ -86,7 +91,7 @@ public abstract class DisplayAreaPolicy { @Override public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, RootDisplayArea root, - DisplayArea<? extends WindowContainer> imeContainer) { + DisplayArea.Tokens imeContainer) { final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService, "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER); final List<TaskDisplayArea> tdaList = new ArrayList<>(); @@ -151,7 +156,7 @@ public abstract class DisplayAreaPolicy { * @see DisplayAreaPolicy#DisplayAreaPolicy */ DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, - RootDisplayArea root, DisplayArea<? extends WindowContainer> imeContainer); + RootDisplayArea root, DisplayArea.Tokens imeContainer); /** * Instantiates the device-specific {@link Provider}. diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index 6a420874b924..c8fadf62eb2f 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST; import android.annotation.Nullable; import android.os.Bundle; @@ -136,12 +137,12 @@ class DisplayAreaPolicyBuilder { private ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>(); /** - * When a window is created, the policy will use this function to select the - * {@link RootDisplayArea} to place that window in. The selected root can be either the one of - * the {@link #mRootHierarchyBuilder} or the one of any of the + * When a window is created, the policy will use this function, which takes window type and + * options, to select the {@link RootDisplayArea} to place that window in. The selected root + * can be either the one of the {@link #mRootHierarchyBuilder} or the one of any of the * {@link #mDisplayAreaGroupHierarchyBuilders}. **/ - @Nullable private BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc; + @Nullable private BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc; /** Defines the root hierarchy for the whole logical display. */ DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) { @@ -161,37 +162,53 @@ class DisplayAreaPolicyBuilder { /** The policy will use this function to find the root to place windows in. */ DisplayAreaPolicyBuilder setSelectRootForWindowFunc( - BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc) { + BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) { mSelectRootForWindowFunc = selectRootForWindowFunc; return this; } - /** Makes sure the setting meets the requirement. */ + /** + * Makes sure the setting meets the requirement: + * 1. {@link mRootHierarchyBuilder} must be set. + * 2. {@link RootDisplayArea} and {@link TaskDisplayArea} must have unique ids. + * 3. {@link Feature} below the same {@link RootDisplayArea} must have unique ids. + * 4. There must be exactly one {@link HierarchyBuilder} that contains the IME container. + * 5. There must be exactly one {@link HierarchyBuilder} that contains the default + * {@link TaskDisplayArea} with id {@link FEATURE_DEFAULT_TASK_CONTAINER}. + * 6. None of the ids is greater than {@link FEATURE_VENDOR_LAST}. + */ private void validate() { if (mRootHierarchyBuilder == null) { throw new IllegalStateException("Root must be set for the display area policy."); } - final Set<Integer> rootIdSet = new ArraySet<>(); - rootIdSet.add(mRootHierarchyBuilder.mRoot.mFeatureId); + final Set<Integer> uniqueIdSet = new ArraySet<>(); + final Set<Integer> allIdSet = new ArraySet<>(); + validateIds(mRootHierarchyBuilder, uniqueIdSet, allIdSet); boolean containsImeContainer = mRootHierarchyBuilder.mImeContainer != null; boolean containsDefaultTda = containsDefaultTaskDisplayArea(mRootHierarchyBuilder); for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) { HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i); - if (!rootIdSet.add(hierarchyBuilder.mRoot.mFeatureId)) { - throw new IllegalStateException("There should not be two RootDisplayAreas with id " - + hierarchyBuilder.mRoot.mFeatureId); - } + validateIds(hierarchyBuilder, uniqueIdSet, allIdSet); + if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) { throw new IllegalStateException( "DisplayAreaGroup must contain at least one TaskDisplayArea."); } - containsImeContainer = containsImeContainer || hierarchyBuilder.mImeContainer != null; + if (containsImeContainer) { + if (hierarchyBuilder.mImeContainer != null) { + throw new IllegalStateException( + "Only one DisplayArea hierarchy can contain the IME container"); + } + } else { + containsImeContainer = hierarchyBuilder.mImeContainer != null; + } + if (containsDefaultTda) { if (containsDefaultTaskDisplayArea(hierarchyBuilder)) { throw new IllegalStateException("Only one TaskDisplayArea can have the feature " - + "of FEATURE_DEFAULT_TASK_CONTAINER"); + + "id of FEATURE_DEFAULT_TASK_CONTAINER"); } } else { containsDefaultTda = containsDefaultTaskDisplayArea(hierarchyBuilder); @@ -203,7 +220,8 @@ class DisplayAreaPolicyBuilder { } if (!containsDefaultTda) { - throw new IllegalStateException("There must be a default TaskDisplayArea."); + throw new IllegalStateException("There must be a default TaskDisplayArea with id of " + + "FEATURE_DEFAULT_TASK_CONTAINER."); } } @@ -218,6 +236,67 @@ class DisplayAreaPolicyBuilder { return false; } + /** + * Makes sure that ids meet requirement. + * {@link RootDisplayArea} and {@link TaskDisplayArea} must have unique ids. + * {@link Feature} below the same {@link RootDisplayArea} must have unique ids, but + * {@link Feature} below different {@link RootDisplayArea} can have the same id so that we can + * organize them together. + * None of the ids is greater than {@link FEATURE_VENDOR_LAST} + * + * @param uniqueIdSet ids of {@link RootDisplayArea} and {@link TaskDisplayArea} that must be + * unique, + * @param allIdSet ids of {@link RootDisplayArea}, {@link TaskDisplayArea} and {@link Feature}. + */ + private static void validateIds(HierarchyBuilder displayAreaHierarchy, + Set<Integer> uniqueIdSet, Set<Integer> allIdSet) { + // Root must have unique id. + final int rootId = displayAreaHierarchy.mRoot.mFeatureId; + if (!allIdSet.add(rootId) || !uniqueIdSet.add(rootId)) { + throw new IllegalStateException( + "RootDisplayArea must have unique id, but id=" + rootId + " is not unique."); + } + if (rootId > FEATURE_VENDOR_LAST) { + throw new IllegalStateException( + "RootDisplayArea should not have an id greater than FEATURE_VENDOR_LAST."); + } + + // TDAs must have unique id. + for (int i = 0; i < displayAreaHierarchy.mTaskDisplayAreas.size(); i++) { + final int taskDisplayAreaId = displayAreaHierarchy.mTaskDisplayAreas.get(i).mFeatureId; + if (!allIdSet.add(taskDisplayAreaId) || !uniqueIdSet.add(taskDisplayAreaId)) { + throw new IllegalStateException("TaskDisplayArea must have unique id, but id=" + + taskDisplayAreaId + " is not unique."); + } + if (taskDisplayAreaId > FEATURE_VENDOR_LAST) { + throw new IllegalStateException("TaskDisplayArea declared in the policy should not" + + "have an id greater than FEATURE_VENDOR_LAST."); + } + } + + // Features below the same root must have unique ids. + final Set<Integer> featureIdSet = new ArraySet<>(); + for (int i = 0; i < displayAreaHierarchy.mFeatures.size(); i++) { + final int featureId = displayAreaHierarchy.mFeatures.get(i).getId(); + if (uniqueIdSet.contains(featureId)) { + throw new IllegalStateException("Feature must not have same id with any " + + "RootDisplayArea or TaskDisplayArea, but id=" + featureId + " is used"); + } + if (!featureIdSet.add(featureId)) { + throw new IllegalStateException("Feature below the same root must have unique id, " + + "but id=" + featureId + " is not unique."); + } + if (featureId > FEATURE_VENDOR_LAST) { + throw new IllegalStateException( + "Feature should not have an id greater than FEATURE_VENDOR_LAST."); + } + } + + // Features below different roots can have the same id so that we can organize them + // together. + allIdSet.addAll(featureIdSet); + } + Result build(WindowManagerService wmService) { validate(); @@ -247,7 +326,7 @@ class DisplayAreaPolicyBuilder { private final ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures = new ArrayList<>(); private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>(); @Nullable - private DisplayArea<? extends WindowContainer> mImeContainer; + private DisplayArea.Tokens mImeContainer; HierarchyBuilder(RootDisplayArea root) { mRoot = root; @@ -270,7 +349,7 @@ class DisplayAreaPolicyBuilder { } /** Sets IME container as a child of this hierarchy root. */ - HierarchyBuilder setImeContainer(DisplayArea<? extends WindowContainer> imeContainer) { + HierarchyBuilder setImeContainer(DisplayArea.Tokens imeContainer) { mImeContainer = imeContainer; return this; } @@ -576,19 +655,19 @@ class DisplayAreaPolicyBuilder { static class Result extends DisplayAreaPolicy { final List<RootDisplayArea> mDisplayAreaGroupRoots; - final BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc; + final BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc; private final TaskDisplayArea mDefaultTaskDisplayArea; Result(WindowManagerService wmService, RootDisplayArea root, List<RootDisplayArea> displayAreaGroupRoots, - @Nullable BiFunction<WindowToken, Bundle, RootDisplayArea> + @Nullable BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) { super(wmService, root); mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots); mSelectRootForWindowFunc = selectRootForWindowFunc == null // Always return the highest level root of the logical display when the func is // not specified. - ? (window, options) -> mRoot + ? (type, options) -> mRoot : selectRootForWindowFunc; // Cache the default TaskDisplayArea for quick access. @@ -610,7 +689,8 @@ class DisplayAreaPolicyBuilder { @VisibleForTesting DisplayArea.Tokens findAreaForToken(WindowToken token) { - return mSelectRootForWindowFunc.apply(token, token.mOptions).findAreaForToken(token); + return mSelectRootForWindowFunc.apply(token.windowType, token.mOptions) + .findAreaForToken(token); } @VisibleForTesting @@ -648,6 +728,13 @@ class DisplayAreaPolicyBuilder { public TaskDisplayArea getDefaultTaskDisplayArea() { return mDefaultTaskDisplayArea; } + + @Override + public DisplayArea.Tokens getDisplayAreaForWindowToken(int type, Bundle options, + boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) { + return mSelectRootForWindowFunc.apply(type, options).findAreaForToken(type, + ownerCanManageAppTokens, roundedCornerOverlay); + } } static class PendingArea { @@ -706,6 +793,10 @@ class DisplayAreaPolicyBuilder { private DisplayArea createArea(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer) { if (mExisting != null) { + if (mExisting.asTokens() != null) { + // Store the WindowToken container for layers + fillAreaForLayers(mExisting.asTokens(), areaForLayer); + } return mExisting; } if (mSkipTokens) { @@ -722,14 +813,18 @@ class DisplayAreaPolicyBuilder { if (mFeature == null) { final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type, "Leaf:" + mMinLayer + ":" + mMaxLayer); - for (int i = mMinLayer; i <= mMaxLayer; i++) { - areaForLayer[i] = leaf; - } + fillAreaForLayers(leaf, areaForLayer); return leaf; } else { return mFeature.mNewDisplayAreaSupplier.create(parent.mWmService, type, mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer, mFeature.mId); } } + + private void fillAreaForLayers(DisplayArea.Tokens leaf, DisplayArea.Tokens[] areaForLayer) { + for (int i = mMinLayer; i <= mMaxLayer; i++) { + areaForLayer[i] = leaf; + } + } } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 1d2cd0a0a350..e88f8e390833 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -165,6 +165,7 @@ import android.graphics.Region.Op; import android.hardware.display.DisplayManagerInternal; import android.metrics.LogMaker; import android.os.Binder; +import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.IBinder; @@ -488,8 +489,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ ActivityRecord mFocusedApp = null; - /** The last focused {@link TaskDisplayArea} on this display. */ - private TaskDisplayArea mLastFocusedTaskDisplayArea = null; + /** + * We only respect the orientation request from apps below this {@link TaskDisplayArea}. + * It is the last focused {@link TaskDisplayArea} on this display that handles orientation + * request. + */ + @Nullable + private TaskDisplayArea mOrientationRequestingTaskDisplayArea = null; /** * The launching activity which is using fixed rotation transformation. @@ -995,7 +1001,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp getPendingTransaction().apply(); // Setup the policy and build the display area hierarchy. - mDisplayAreaPolicy = mWmService.mDisplayAreaPolicyProvider.instantiate( + mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate( mWmService, this /* content */, this /* root */, mImeWindowsContainers); // Sets the display content for the children. @@ -3325,7 +3331,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Called even if the focused app is not changed in case the app is moved to a different // TaskDisplayArea. - setLastFocusedTaskDisplayArea(newFocus.getDisplayArea()); + onLastFocusedTaskDisplayAreaChanged(newFocus.getDisplayArea()); } if (mFocusedApp == newFocus) { return false; @@ -3339,16 +3345,27 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } /** Called when the focused {@link TaskDisplayArea} on this display may have changed. */ - @VisibleForTesting - void setLastFocusedTaskDisplayArea(@Nullable TaskDisplayArea taskDisplayArea) { - if (taskDisplayArea != null) { - mLastFocusedTaskDisplayArea = taskDisplayArea; + void onLastFocusedTaskDisplayAreaChanged(@Nullable TaskDisplayArea taskDisplayArea) { + // Only record the TaskDisplayArea that handles orientation request. + if (taskDisplayArea != null && taskDisplayArea.handlesOrientationChangeFromDescendant()) { + mOrientationRequestingTaskDisplayArea = taskDisplayArea; + return; + } + + // If the previous TDA no longer handles orientation request, clear it. + if (mOrientationRequestingTaskDisplayArea != null + && !mOrientationRequestingTaskDisplayArea + .handlesOrientationChangeFromDescendant()) { + mOrientationRequestingTaskDisplayArea = null; } } - /** Gets the last focused {@link TaskDisplayArea} on this display. */ - TaskDisplayArea getLastFocusedTaskDisplayArea() { - return mLastFocusedTaskDisplayArea; + /** + * Gets the {@link TaskDisplayArea} that we respect orientation requests from apps below it. + */ + @Nullable + TaskDisplayArea getOrientationRequestingTaskDisplayArea() { + return mOrientationRequestingTaskDisplayArea; } /** Updates the layer assignment of windows on this display. */ @@ -4775,7 +4792,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return getWindowContainers().getSurfaceControl(); } - @VisibleForTesting DisplayArea.Tokens getImeContainer() { return mImeWindowsContainers; } @@ -5683,6 +5699,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override public boolean getRequestedVisibility(@InternalInsetsType int type) { + if (type == ITYPE_IME) { + return getInsetsStateController().getImeSourceProvider().isImeShowing(); + } return mRequestedInsetsState.getSourceOrDefaultVisibility(type); } @@ -5728,4 +5747,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp MagnificationSpec getMagnificationSpec() { return mMagnificationSpec; } + + DisplayArea getAreaForWindowToken(int windowType, Bundle options, + boolean ownerCanManageAppToken, boolean roundedCornerOverlay) { + // TODO(b/159767464): figure out how to find an appropriate TDA. + if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) { + return getDefaultTaskDisplayArea(); + } + // Return IME container here because it could be in one of sub RootDisplayAreas depending on + // the focused edit text. Also, the RootDisplayArea choosing strategy is implemented by + // the server side, but not mSelectRootForWindowFunc customized by OEM. + if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) { + return getImeContainer(); + } + return mDisplayAreaPolicy.getDisplayAreaForWindowToken(windowType, options, + ownerCanManageAppToken, roundedCornerOverlay); + } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 2ddd00101769..cd02e00c4383 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1427,7 +1427,7 @@ public class DisplayPolicy { : (task != null ? task.getBounds() : null); final InsetsState state = mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs); - computeWindowBounds(attrs, state, outFrame); + computeWindowBounds(attrs, state, windowToken, outFrame); if (taskBounds != null) { outFrame.intersect(taskBounds); } @@ -1714,11 +1714,11 @@ public class DisplayPolicy { } private void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state, - Rect outBounds) { + @Nullable WindowToken windowToken, Rect outBounds) { final @InsetsType int typesToFit = attrs.getFitInsetsTypes(); final @InsetsSide int sidesToFit = attrs.getFitInsetsSides(); final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit); - final Rect dfu = state.getDisplayFrame(); + final Rect df = windowToken != null ? windowToken.getBounds() : state.getDisplayFrame(); Insets insets = Insets.of(0, 0, 0, 0); for (int i = types.size() - 1; i >= 0; i--) { final InsetsSource source = state.peekSource(types.valueAt(i)); @@ -1726,13 +1726,13 @@ public class DisplayPolicy { continue; } insets = Insets.max(insets, source.calculateInsets( - dfu, attrs.isFitInsetsIgnoringVisibility())); + df, attrs.isFitInsetsIgnoringVisibility())); } final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0; final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0; final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0; final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0; - outBounds.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom); + outBounds.set(df.left + left, df.top + top, df.right - right, df.bottom - bottom); } /** @@ -1772,7 +1772,7 @@ public class DisplayPolicy { final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; final InsetsState state = win.getInsetsState(); - computeWindowBounds(attrs, state, df); + computeWindowBounds(attrs, state, win.mToken, df); if (attached == null) { pf.set(df); if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) { @@ -1845,7 +1845,6 @@ public class DisplayPolicy { // They will later be cropped or shifted using the displayFrame in WindowState, // which prevents overlap with the DisplayCutout. if (!attachedInParent && !floatingInScreenWindow) { - getRotatedWindowBounds(displayFrames, win, sTmpRect); sTmpRect.set(pf); pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars); windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf)); diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index aa603aba0f78..57c947f572d9 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -16,9 +16,9 @@ package com.android.server.wm; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -64,7 +64,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { ? "DisplayWindowSettingsProvider" : TAG_WM; private static final String DATA_DISPLAY_SETTINGS_FILE_PATH = "system/display_settings.xml"; - private static final String VENDOR_DISPLAY_SETTINGS_PATH = "etc/display_settings.xml"; + private static final String VENDOR_DISPLAY_SETTINGS_FILE_PATH = "etc/display_settings.xml"; private static final String WM_DISPLAY_COMMIT_TAG = "wm-displays"; private static final int IDENTIFIER_UNIQUE_ID = 0; @@ -86,34 +86,8 @@ class DisplayWindowSettingsProvider implements SettingsProvider { void finishWrite(OutputStream os, boolean success); } - private final ReadableSettingsStorage mVendorSettingsStorage; - /** - * The preferred type of a display identifier to use when storing and retrieving entries from - * the base (vendor) settings file. - * - * @see #getIdentifier(DisplayInfo, int) - */ - @DisplayIdentifierType - private int mVendorIdentifierType; - private final Map<String, SettingsEntry> mVendorSettings = new HashMap<>(); - - private final WritableSettingsStorage mOverrideSettingsStorage; - /** - * The preferred type of a display identifier to use when storing and retrieving entries from - * the data (override) settings file. - * - * @see #getIdentifier(DisplayInfo, int) - */ - @DisplayIdentifierType - private int mOverrideIdentifierType; - private final Map<String, SettingsEntry> mOverrideSettings = new HashMap<>(); - - /** - * Enables or disables settings provided from the vendor settings storage. - * - * @see #setVendorSettingsIgnored(boolean) - */ - private boolean mIgnoreVendorSettings = true; + private ReadableSettings mBaseSettings; + private final WritableSettings mOverrideSettings; DisplayWindowSettingsProvider() { this(new AtomicFileStorage(getVendorSettingsFile()), @@ -121,43 +95,48 @@ class DisplayWindowSettingsProvider implements SettingsProvider { } @VisibleForTesting - DisplayWindowSettingsProvider(@NonNull ReadableSettingsStorage vendorSettingsStorage, + DisplayWindowSettingsProvider(@NonNull ReadableSettingsStorage baseSettingsStorage, @NonNull WritableSettingsStorage overrideSettingsStorage) { - mVendorSettingsStorage = vendorSettingsStorage; - mOverrideSettingsStorage = overrideSettingsStorage; - readSettings(); + mBaseSettings = new ReadableSettings(baseSettingsStorage); + mOverrideSettings = new WritableSettings(overrideSettingsStorage); } /** - * Enables or disables settings provided from the vendor settings storage. If {@code true}, the - * vendor settings will be ignored and only the override settings will be returned from - * {@link #getSettings(DisplayInfo)}. If {@code false}, settings returned from - * {@link #getSettings(DisplayInfo)} will be a merged result of the vendor settings and the - * override settings. + * Overrides the path for the file that should be used to read base settings. If {@code null} is + * passed the default base settings file path will be used. + * + * @see #VENDOR_DISPLAY_SETTINGS_FILE_PATH */ - void setVendorSettingsIgnored(boolean ignored) { - mIgnoreVendorSettings = ignored; + void setBaseSettingsFilePath(@Nullable String path) { + AtomicFile settingsFile; + if (path != null) { + settingsFile = new AtomicFile(new File(path), WM_DISPLAY_COMMIT_TAG); + } else { + settingsFile = getVendorSettingsFile(); + } + + setBaseSettingsStorage(new AtomicFileStorage(settingsFile)); } /** - * Returns whether or not the vendor settings are being ignored. + * Overrides the storage that should be used to read base settings. * - * @see #setVendorSettingsIgnored(boolean) + * @see #setBaseSettingsFilePath(String) */ @VisibleForTesting - boolean getVendorSettingsIgnored() { - return mIgnoreVendorSettings; + void setBaseSettingsStorage(@NonNull ReadableSettingsStorage baseSettingsStorage) { + mBaseSettings = new ReadableSettings(baseSettingsStorage); } @Override @NonNull public SettingsEntry getSettings(@NonNull DisplayInfo info) { - SettingsEntry vendorSettings = getVendorSettingsEntry(info); - SettingsEntry overrideSettings = getOrCreateOverrideSettingsEntry(info); - if (vendorSettings == null) { + SettingsEntry baseSettings = mBaseSettings.getSettingsEntry(info); + SettingsEntry overrideSettings = mOverrideSettings.getOrCreateSettingsEntry(info); + if (baseSettings == null) { return new SettingsEntry(overrideSettings); } else { - SettingsEntry mergedSettings = new SettingsEntry(vendorSettings); + SettingsEntry mergedSettings = new SettingsEntry(baseSettings); mergedSettings.updateFrom(overrideSettings); return mergedSettings; } @@ -166,99 +145,126 @@ class DisplayWindowSettingsProvider implements SettingsProvider { @Override @NonNull public SettingsEntry getOverrideSettings(@NonNull DisplayInfo info) { - return new SettingsEntry(getOrCreateOverrideSettingsEntry(info)); + return new SettingsEntry(mOverrideSettings.getOrCreateSettingsEntry(info)); } @Override public void updateOverrideSettings(@NonNull DisplayInfo info, @NonNull SettingsEntry overrides) { - final SettingsEntry overrideSettings = getOrCreateOverrideSettingsEntry(info); - boolean changed = overrideSettings.setTo(overrides); - if (changed) { - writeOverrideSettings(); - } + mOverrideSettings.updateSettingsEntry(info, overrides); } - @Nullable - private SettingsEntry getVendorSettingsEntry(DisplayInfo info) { - if (mIgnoreVendorSettings) { + /** + * Class that allows reading {@link SettingsEntry entries} from a + * {@link ReadableSettingsStorage}. + */ + private static class ReadableSettings { + /** + * The preferred type of a display identifier to use when storing and retrieving entries + * from the settings entries. + * + * @see #getIdentifier(DisplayInfo) + */ + @DisplayIdentifierType + protected int mIdentifierType; + protected final Map<String, SettingsEntry> mSettings = new HashMap<>(); + + ReadableSettings(ReadableSettingsStorage settingsStorage) { + loadSettings(settingsStorage); + } + + @Nullable + final SettingsEntry getSettingsEntry(DisplayInfo info) { + final String identifier = getIdentifier(info); + SettingsEntry settings; + // Try to get corresponding settings using preferred identifier for the current config. + if ((settings = mSettings.get(identifier)) != null) { + return settings; + } + // Else, fall back to the display name. + if ((settings = mSettings.get(info.name)) != null) { + // Found an entry stored with old identifier. + mSettings.remove(info.name); + mSettings.put(identifier, settings); + return settings; + } return null; } - final String identifier = getIdentifier(info, mVendorIdentifierType); - SettingsEntry settings; - // Try to get corresponding settings using preferred identifier for the current config. - if ((settings = mVendorSettings.get(identifier)) != null) { - return settings; + /** Gets the identifier of choice for the current config. */ + protected final String getIdentifier(DisplayInfo displayInfo) { + if (mIdentifierType == IDENTIFIER_PORT && displayInfo.address != null) { + // Config suggests using port as identifier for physical displays. + if (displayInfo.address instanceof DisplayAddress.Physical) { + return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort(); + } + } + return displayInfo.uniqueId; } - // Else, fall back to the display name. - if ((settings = mVendorSettings.get(info.name)) != null) { - // Found an entry stored with old identifier. - mVendorSettings.remove(info.name); - mVendorSettings.put(identifier, settings); - return settings; + + private void loadSettings(ReadableSettingsStorage settingsStorage) { + FileData fileData = readSettings(settingsStorage); + if (fileData != null) { + mIdentifierType = fileData.mIdentifierType; + mSettings.putAll(fileData.mSettings); + } } - return null; } - @NonNull - private SettingsEntry getOrCreateOverrideSettingsEntry(DisplayInfo info) { - final String identifier = getIdentifier(info, mOverrideIdentifierType); - SettingsEntry settings; - // Try to get corresponding settings using preferred identifier for the current config. - if ((settings = mOverrideSettings.get(identifier)) != null) { - return settings; - } - // Else, fall back to the display name. - if ((settings = mOverrideSettings.get(info.name)) != null) { - // Found an entry stored with old identifier. - mOverrideSettings.remove(info.name); - mOverrideSettings.put(identifier, settings); - writeOverrideSettings(); - return settings; + /** + * Class that allows reading {@link SettingsEntry entries} from, and writing entries to, a + * {@link WritableSettingsStorage}. + */ + private static final class WritableSettings extends ReadableSettings { + private final WritableSettingsStorage mSettingsStorage; + + WritableSettings(WritableSettingsStorage settingsStorage) { + super(settingsStorage); + mSettingsStorage = settingsStorage; } - settings = new SettingsEntry(); - mOverrideSettings.put(identifier, settings); - return settings; - } + @NonNull + SettingsEntry getOrCreateSettingsEntry(DisplayInfo info) { + final String identifier = getIdentifier(info); + SettingsEntry settings; + // Try to get corresponding settings using preferred identifier for the current config. + if ((settings = mSettings.get(identifier)) != null) { + return settings; + } + // Else, fall back to the display name. + if ((settings = mSettings.get(info.name)) != null) { + // Found an entry stored with old identifier. + mSettings.remove(info.name); + mSettings.put(identifier, settings); + writeSettings(); + return settings; + } - private void readSettings() { - FileData vendorFileData = readSettings(mVendorSettingsStorage); - if (vendorFileData != null) { - mVendorIdentifierType = vendorFileData.mIdentifierType; - mVendorSettings.putAll(vendorFileData.mSettings); + settings = new SettingsEntry(); + mSettings.put(identifier, settings); + return settings; } - FileData overrideFileData = readSettings(mOverrideSettingsStorage); - if (overrideFileData != null) { - mOverrideIdentifierType = overrideFileData.mIdentifierType; - mOverrideSettings.putAll(overrideFileData.mSettings); + void updateSettingsEntry(DisplayInfo info, SettingsEntry settings) { + final SettingsEntry overrideSettings = getOrCreateSettingsEntry(info); + final boolean changed = overrideSettings.setTo(settings); + if (changed) { + writeSettings(); + } } - } - - private void writeOverrideSettings() { - FileData fileData = new FileData(); - fileData.mIdentifierType = mOverrideIdentifierType; - fileData.mSettings.putAll(mOverrideSettings); - writeSettings(mOverrideSettingsStorage, fileData); - } - /** Gets the identifier of choice for the current config. */ - private static String getIdentifier(DisplayInfo displayInfo, @DisplayIdentifierType int type) { - if (type == IDENTIFIER_PORT && displayInfo.address != null) { - // Config suggests using port as identifier for physical displays. - if (displayInfo.address instanceof DisplayAddress.Physical) { - return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort(); - } + private void writeSettings() { + FileData fileData = new FileData(); + fileData.mIdentifierType = mIdentifierType; + fileData.mSettings.putAll(mSettings); + DisplayWindowSettingsProvider.writeSettings(mSettingsStorage, fileData); } - return displayInfo.uniqueId; } @NonNull private static AtomicFile getVendorSettingsFile() { final File vendorFile = new File(Environment.getVendorDirectory(), - VENDOR_DISPLAY_SETTINGS_PATH); + VENDOR_DISPLAY_SETTINGS_FILE_PATH); return new AtomicFile(vendorFile, WM_DISPLAY_COMMIT_TAG); } @@ -444,12 +450,12 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.attributeInt(null, "windowingMode", settingsEntry.mWindowingMode); } if (settingsEntry.mUserRotationMode != null) { - out.attribute(null, "userRotationMode", - settingsEntry.mUserRotationMode.toString()); + out.attributeInt(null, "userRotationMode", + settingsEntry.mUserRotationMode); } if (settingsEntry.mUserRotation != null) { - out.attribute(null, "userRotation", - settingsEntry.mUserRotation.toString()); + out.attributeInt(null, "userRotation", + settingsEntry.mUserRotation); } if (settingsEntry.mForcedWidth != 0 && settingsEntry.mForcedHeight != 0) { out.attributeInt(null, "forcedWidth", settingsEntry.mForcedWidth); @@ -459,30 +465,30 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.attributeInt(null, "forcedDensity", settingsEntry.mForcedDensity); } if (settingsEntry.mForcedScalingMode != null) { - out.attribute(null, "forcedScalingMode", - settingsEntry.mForcedScalingMode.toString()); + out.attributeInt(null, "forcedScalingMode", + settingsEntry.mForcedScalingMode); } if (settingsEntry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) { out.attributeInt(null, "removeContentMode", settingsEntry.mRemoveContentMode); } if (settingsEntry.mShouldShowWithInsecureKeyguard != null) { - out.attribute(null, "shouldShowWithInsecureKeyguard", - settingsEntry.mShouldShowWithInsecureKeyguard.toString()); + out.attributeBoolean(null, "shouldShowWithInsecureKeyguard", + settingsEntry.mShouldShowWithInsecureKeyguard); } if (settingsEntry.mShouldShowSystemDecors != null) { - out.attribute(null, "shouldShowSystemDecors", - settingsEntry.mShouldShowSystemDecors.toString()); + out.attributeBoolean(null, "shouldShowSystemDecors", + settingsEntry.mShouldShowSystemDecors); } if (settingsEntry.mImePolicy != null) { out.attributeInt(null, "imePolicy", settingsEntry.mImePolicy); } if (settingsEntry.mFixedToUserRotation != null) { - out.attribute(null, "fixedToUserRotation", - settingsEntry.mFixedToUserRotation.toString()); + out.attributeInt(null, "fixedToUserRotation", + settingsEntry.mFixedToUserRotation); } if (settingsEntry.mIgnoreOrientationRequest != null) { - out.attribute(null, "ignoreOrientationRequest", - settingsEntry.mIgnoreOrientationRequest.toString()); + out.attributeBoolean(null, "ignoreOrientationRequest", + settingsEntry.mIgnoreOrientationRequest); } out.endTag(null, "display"); } diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java index 14880ed30f24..9602880486b5 100644 --- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java +++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import android.app.ActivityTaskManager; import android.app.UriGrantsManager; import android.content.ClipData; import android.net.Uri; @@ -33,6 +32,7 @@ import java.util.ArrayList; class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub implements IBinder.DeathRecipient { + private final WindowManagerGlobalLock mGlobalLock; private final int mSourceUid; private final String mTargetPackage; private final int mMode; @@ -45,8 +45,9 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub private IBinder mPermissionOwnerToken = null; private IBinder mTransientToken = null; - DragAndDropPermissionsHandler(ClipData clipData, int sourceUid, String targetPackage, int mode, - int sourceUserId, int targetUserId) { + DragAndDropPermissionsHandler(WindowManagerGlobalLock lock, ClipData clipData, int sourceUid, + String targetPackage, int mode, int sourceUserId, int targetUserId) { + mGlobalLock = lock; mSourceUid = sourceUid; mTargetPackage = targetPackage; mMode = mode; @@ -64,8 +65,7 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub mActivityToken = activityToken; // Will throw if Activity is not found. - IBinder permissionOwner = ActivityTaskManager.getService(). - getUriPermissionOwnerForActivity(mActivityToken); + IBinder permissionOwner = getUriPermissionOwnerForActivity(mActivityToken); doTake(permissionOwner); } @@ -105,8 +105,7 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub IBinder permissionOwner = null; if (mActivityToken != null) { try { - permissionOwner = ActivityTaskManager.getService(). - getUriPermissionOwnerForActivity(mActivityToken); + permissionOwner = getUriPermissionOwnerForActivity(mActivityToken); } catch (Exception e) { // Activity is destroyed, permissions already revoked. return; @@ -126,6 +125,18 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub } } + private IBinder getUriPermissionOwnerForActivity(IBinder activityToken) { + ActivityTaskManagerService.enforceNotIsolatedCaller("getUriPermissionOwnerForActivity"); + synchronized (mGlobalLock) { + ActivityRecord r = ActivityRecord.isInStackLocked(activityToken); + if (r == null) { + throw new IllegalArgumentException("Activity does not exist; token=" + + activityToken); + } + return r.getUriPermissionsLocked().getExternalToken(); + } + } + @Override public void binderDied() { try { diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index ad4e64a08183..86518ea4ccc1 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -59,7 +59,6 @@ import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; -import com.android.internal.os.SomeArgs; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.view.IDragAndDropPermissions; import com.android.server.LocalServices; @@ -590,7 +589,7 @@ class DragState { final DragAndDropPermissionsHandler dragAndDropPermissions; if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0 && mData != null) { - dragAndDropPermissions = new DragAndDropPermissionsHandler( + dragAndDropPermissions = new DragAndDropPermissionsHandler(mService.mGlobalLock, mData, mUid, touchedWin.getOwningPackage(), diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 818d96ceb5a6..91106eff9805 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -42,6 +42,7 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { private InsetsControlTarget mImeTargetFromIme; private Runnable mShowImeRunner; private boolean mIsImeLayoutDrawn; + private boolean mImeShowing; ImeInsetsSourceProvider(InsetsSource source, InsetsStateController stateController, DisplayContent displayContent) { @@ -81,6 +82,7 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s", target.getWindow() != null ? target.getWindow().getName() : ""); + setImeShowing(true); target.showInsets(WindowInsets.Type.ime(), true /* fromIme */); Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0); if (target != mImeTargetFromIme && mImeTargetFromIme != null) { @@ -155,12 +157,14 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { @Override public void dump(PrintWriter pw, String prefix) { super.dump(pw, prefix); + pw.print(prefix); + pw.print("mImeShowing="); + pw.print(mImeShowing); if (mImeTargetFromIme != null) { - pw.print(prefix); - pw.print("showImePostLayout pending for mImeTargetFromIme="); + pw.print(" showImePostLayout pending for mImeTargetFromIme="); pw.print(mImeTargetFromIme); - pw.println(); } + pw.println(); } @Override @@ -173,4 +177,20 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { proto.write(IS_IME_LAYOUT_DRAWN, mIsImeLayoutDrawn); proto.end(token); } + + /** + * Sets whether the IME is currently supposed to be showing according to + * InputMethodManagerService. + */ + public void setImeShowing(boolean imeShowing) { + mImeShowing = imeShowing; + } + + /** + * Returns whether the IME is currently supposed to be showing according to + * InputMethodManagerService. + */ + public boolean isImeShowing() { + return mImeShowing; + } } diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 0605ed800565..28a99825d0b4 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -328,7 +328,7 @@ class InsetsSourceProvider { final Point surfacePosition = getWindowFrameSurfacePosition(); mAdapter = new ControlAdapter(surfacePosition); if (getSource().getType() == ITYPE_IME) { - setClientVisible(InsetsState.getDefaultVisibility(mSource.getType())); + setClientVisible(target.getRequestedVisibility(mSource.getType())); } final Transaction t = mDisplayContent.getPendingTransaction(); mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */, diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index 52ada472f0aa..fd42b2467d19 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -29,7 +29,6 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; import android.view.DisplayInfo; -import android.view.IPinnedStackController; import android.view.IPinnedStackListener; import java.io.PrintWriter; @@ -63,8 +62,6 @@ class PinnedStackController { private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler = new PinnedStackListenerDeathHandler(); - private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback(); - /** Whether the PiP is entering or leaving. */ private boolean mIsPipWindowingModeChanging; @@ -86,18 +83,6 @@ class PinnedStackController { private final DisplayMetrics mTmpMetrics = new DisplayMetrics(); /** - * The callback object passed to listeners for them to notify the controller of state changes. - */ - private class PinnedStackControllerCallback extends IPinnedStackController.Stub { - @Override - public int getDisplayRotation() { - synchronized (mService.mGlobalLock) { - return mDisplayInfo.rotation; - } - } - } - - /** * Handler for the case where the listener dies. */ private class PinnedStackListenerDeathHandler implements IBinder.DeathRecipient { @@ -141,7 +126,6 @@ class PinnedStackController { void registerPinnedStackListener(IPinnedStackListener listener) { try { listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0); - listener.onListenerRegistered(mCallbacks); mPinnedStackListener = listener; notifyDisplayInfoChanged(mDisplayInfo); notifyImeVisibilityChanged(mIsImeShowing, mImeHeight); diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java index da04f438a496..393055eab111 100644 --- a/services/core/java/com/android/server/wm/RootDisplayArea.java +++ b/services/core/java/com/android/server/wm/RootDisplayArea.java @@ -16,11 +16,17 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; +import android.annotation.Nullable; + +import com.android.server.policy.WindowManagerPolicy; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -72,7 +78,8 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { * match the root. */ void placeImeContainer(DisplayArea.Tokens imeContainer) { - if (imeContainer.getRootDisplayArea() == this) { + final RootDisplayArea previousRoot = imeContainer.getRootDisplayArea(); + if (previousRoot == this) { // No need to reparent if IME container is below the same root. return; } @@ -88,7 +95,9 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { + "FEATURE_IME_PLACEHOLDER"); } + previousRoot.updateImeContainerForLayers(null /* imeContainer */); imeContainer.reparent(imeDisplayAreas.get(0), POSITION_TOP); + updateImeContainerForLayers(imeContainer); return; } } @@ -97,12 +106,23 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { } /** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */ + @Nullable DisplayArea.Tokens findAreaForToken(WindowToken token) { - int windowLayerFromType = token.getWindowLayerFromType(); + return findAreaForToken(token.windowType, token.mOwnerCanManageAppTokens, + token.mRoundedCornerOverlay); + } + + @Nullable + DisplayArea.Tokens findAreaForToken(int windowType, boolean ownerCanManageAppTokens, + boolean roundedCornerOverlay) { + // TODO(b/159767464): cover TYPE_INPUT_METHOD(_DIALOG) case here. mAreaForLayer doesn't + // contain IME container. + int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, + ownerCanManageAppTokens); if (windowLayerFromType == APPLICATION_LAYER) { throw new IllegalArgumentException( "There shouldn't be WindowToken on APPLICATION_LAYER"); - } else if (token.mRoundedCornerOverlay) { + } else if (roundedCornerOverlay) { windowLayerFromType = mAreaForLayer.length - 1; } return mAreaForLayer[windowLayerFromType]; @@ -119,4 +139,10 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { mAreaForLayer = areaForLayer; mFeatureToDisplayAreas = featureToDisplayAreas; } + + private void updateImeContainerForLayers(@Nullable DisplayArea.Tokens imeContainer) { + final WindowManagerPolicy policy = mWmService.mPolicy; + mAreaForLayer[policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)] = imeContainer; + mAreaForLayer[policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)] = imeContainer; + } } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 7df2b407557d..6e0efbf8bd51 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -26,7 +26,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.util.DebugUtils; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; @@ -388,7 +387,7 @@ class SurfaceAnimator { if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash"); final SurfaceControl.Builder builder = animatable.makeAnimationLeash() .setParent(animatable.getAnimationLeashParent()) - .setName(surface + " - animation-leash") + .setName(surface + " - animation-leash of " + animationTypeToString(type)) // TODO(b/151665759) Defer reparent calls // We want the leash to be visible immediately because the transaction which shows // the leash may be deferred but the reparent will not. This will cause the leashed @@ -430,8 +429,7 @@ class SurfaceAnimator { void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mLeash="); pw.print(mLeash); - pw.print(" mAnimationType=" + DebugUtils.valueToString(SurfaceAnimator.class, - "ANIMATION_TYPE_", mAnimationType)); + pw.print(" mAnimationType=" + animationTypeToString(mAnimationType)); pw.println(mAnimationStartDelayed ? " mAnimationStartDelayed=true" : ""); pw.print(prefix); pw.print("Animation: "); pw.println(mAnimation); if (mAnimation != null) { @@ -513,6 +511,23 @@ class SurfaceAnimator { @interface AnimationType {} /** + * Converts {@link AnimationType} to String. + */ + private static String animationTypeToString(@AnimationType int type) { + switch (type) { + case ANIMATION_TYPE_NONE: return "none"; + case ANIMATION_TYPE_APP_TRANSITION: return "app_transition"; + case ANIMATION_TYPE_SCREEN_ROTATION: return "screen_rotation"; + case ANIMATION_TYPE_DIMMER: return "dimmer"; + case ANIMATION_TYPE_RECENTS: return "recents_animation"; + case ANIMATION_TYPE_WINDOW_ANIMATION: return "window_animation"; + case ANIMATION_TYPE_INSETS_CONTROL: return "insets_animation"; + case ANIMATION_TYPE_FIXED_TRANSFORM: return "fixed_rotation"; + default: return "unknown type:" + type; + } + } + + /** * Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the * component that is running the animation when the animation is finished. */ diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 4498a8c82583..9425602763c5 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -641,9 +641,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { @Override int getOrientation(int candidate) { mLastOrientationSource = null; - // Only allow to specify orientation if this TDA is not set to ignore orientation request, - // and it has the focus. - if (mIgnoreOrientationRequest || !isLastFocused()) { + if (!canSpecifyOrientation()) { return SCREEN_ORIENTATION_UNSET; } @@ -1918,10 +1916,14 @@ final class TaskDisplayArea extends DisplayArea<Task> { return lastReparentedStack; } - /** Whether this task display area is the last focused one on this logical display. */ + /** Whether this task display area can request orientation. */ @VisibleForTesting - boolean isLastFocused() { - return mDisplayContent.getLastFocusedTaskDisplayArea() == this; + boolean canSpecifyOrientation() { + // Only allow to specify orientation if this TDA is not set to ignore orientation request, + // and it is the last focused one on this logical display that can request orientation + // request. + return !mIgnoreOrientationRequest + && mDisplayContent.getOrientationRequestingTaskDisplayArea() == this; } @Override diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index cf6468d66b57..adc7a227861a 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -98,6 +98,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; +import java.util.List; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -314,6 +315,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< final SurfaceControl.Transaction mSyncTransaction; @SyncState int mSyncState = SYNC_STATE_NONE; + private final List<WindowContainerListener> mListeners = new ArrayList<>(); + WindowContainer(WindowManagerService wms) { mWmService = wms; mPendingTransaction = wms.mTransactionFactory.get(); @@ -637,6 +640,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mParent != null) { mParent.removeChild(this); } + + for (int i = mListeners.size() - 1; i >= 0; --i) { + mListeners.get(i).onRemoved(); + } } /** @@ -819,6 +826,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< final WindowContainer child = mChildren.get(i); child.onDisplayChanged(dc); } + for (int i = mListeners.size() - 1; i >= 0; --i) { + mListeners.get(i).onDisplayChanged(dc); + } } DisplayContent getDisplayContent() { @@ -1152,10 +1162,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< /** * Check if this container or its parent will handle orientation changes from descendants. It's - * different from the return value of {@link #onDescendantOrientationChanged(IBinder, - * WindowContainer)} in the sense that the return value of this method tells if this - * container or its parent will handle the request eventually, while the return value of the - * other method is if it handled the request synchronously. + * different from the return value of {@link #onDescendantOrientationChanged(WindowContainer)} + * in the sense that the return value of this method tells if this container or its parent will + * handle the request eventually, while the return value of the other method is if it handled + * the request synchronously. * * @return {@code true} if it handles or will handle orientation change in the future; {@code * false} if it won't handle the change at anytime. @@ -1221,7 +1231,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** - * Calls {@link #setOrientation(int, IBinder, WindowContainer)} with {@code null} to the last 2 + * Calls {@link #setOrientation(int, WindowContainer)} with {@code null} to the last 2 * parameters. * * @param orientation the specified orientation. @@ -3106,4 +3116,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mSyncState = SYNC_STATE_NONE; prepareSync(); } + + void registerWindowContainerListener(WindowContainerListener listener) { + if (mListeners.contains(listener)) { + return; + } + mListeners.add(listener); + // Also register to ConfigurationChangeListener to receive configuration changes. + registerConfigurationChangeListener(listener); + listener.onDisplayChanged(getDisplayContent()); + } + + void unregisterWindowContainerListener(WindowContainerListener listener) { + mListeners.remove(listener); + unregisterConfigurationChangeListener(listener); + } } diff --git a/cmds/statsd/src/experiment_ids.proto b/services/core/java/com/android/server/wm/WindowContainerListener.java index c2036314cf58..ac1fe173dd09 100644 --- a/cmds/statsd/src/experiment_ids.proto +++ b/services/core/java/com/android/server/wm/WindowContainerListener.java @@ -14,16 +14,17 @@ * limitations under the License. */ -syntax = "proto2"; +package com.android.server.wm; -package android.os.statsd; +/** + * Interface for listening to changes in a {@link WindowContainer}. A usage of this listener is + * to receive the changes and propagate them to the client side. + */ +interface WindowContainerListener extends ConfigurationContainerListener { -option java_package = "com.android.internal.os"; -option java_outer_classname = "ExperimentIdsProto"; + /** @see WindowContainer#onDisplayChanged(DisplayContent) */ + default void onDisplayChanged(DisplayContent dc) {} -// StatsLogProcessor uses the proto to parse experiment ids from -// BinaryPushStateChanged atoms. This needs to be in sync with -// TrainExperimentIds in atoms.proto. -message ExperimentIds { - repeated int64 experiment_id = 1; + /** Called when {@link WindowContainer#removeImmediately()} is invoked. */ + default void onRemoved() {} } diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java new file mode 100644 index 000000000000..051ece38b26f --- /dev/null +++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.Display.INVALID_DISPLAY; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; +import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR; + +import android.annotation.NonNull; +import android.app.IWindowToken; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; + +import java.util.Map; +import java.util.Objects; + +/** + * A controller to register/unregister {@link WindowContainerListener} for + * {@link android.app.WindowContext}. + * + * <ul> + * <li>When a {@link android.app.WindowContext} is created, it registers the listener via + * {@link WindowManagerService#registerWindowContextListener(IBinder, int, int, Bundle)} + * automatically.</li> + * <li>When the {@link android.app.WindowContext} adds the first window to the screen via + * {@link android.view.WindowManager#addView(View, ViewGroup.LayoutParams)}, + * {@link WindowManagerService} then updates the {@link WindowContextListenerImpl} to listen + * to corresponding {@link WindowToken} via this controller.</li> + * <li>When the {@link android.app.WindowContext} is GCed, it unregisters the previously + * registered listener via + * {@link WindowManagerService#unregisterWindowContextListener(IBinder)}. + * {@link WindowManagerService} is also responsible for removing the + * {@link android.app.WindowContext} created {@link WindowToken}.</li> + * </ul> + * <p>Note that the listener may be removed earlier than the + * {@link #unregisterWindowContainerListener(IBinder)} if the listened {@link WindowContainer} was + * removed. An example is that the {@link DisplayArea} is removed when users unfold the + * foldable devices. Another example is that the associated external display is detached.</p> + */ +class WindowContextListenerController { + @VisibleForTesting + final Map<IBinder, WindowContextListenerImpl> mListeners = new ArrayMap<>(); + + /** + * Registers the listener to a {@code container} which is associated with + * a {@code clientToken}, which is a {@link android.app.WindowContext} representation. If the + * listener associated with {@code clientToken} hasn't been initialized yet, create one + * {@link WindowContextListenerImpl}. Otherwise, the listener associated with + * {@code clientToken} switches to listen to the {@code container}. + * + * @param clientToken the token to associate with the listener + * @param container the {@link WindowContainer} which the listener is going to listen to. + * @param ownerUid the caller UID + */ + void registerWindowContainerListener(@NonNull IBinder clientToken, + @NonNull WindowContainer container, int ownerUid) { + WindowContextListenerImpl listener = mListeners.get(clientToken); + if (listener == null) { + listener = new WindowContextListenerImpl(clientToken, container, ownerUid); + listener.register(); + } else { + listener.updateContainer(container); + } + } + + void unregisterWindowContainerListener(IBinder clientToken) { + final WindowContextListenerImpl listener = mListeners.get(clientToken); + // Listeners may be removed earlier. An example is the display where the listener is + // located is detached. In this case, all window containers on the display, as well as + // their listeners will be removed before their listeners are unregistered. + if (listener == null) { + return; + } + listener.unregister(); + } + + boolean assertCallerCanRemoveListener(IBinder clientToken, boolean callerCanManageAppTokens, + int callingUid) { + final WindowContextListenerImpl listener = mListeners.get(clientToken); + if (listener == null) { + ProtoLog.i(WM_DEBUG_ADD_REMOVE, "The listener does not exist."); + return false; + } + if (callerCanManageAppTokens) { + return true; + } + if (callingUid != listener.mOwnerUid) { + throw new UnsupportedOperationException("Uid mismatch. Caller uid is " + callingUid + + ", while the listener's owner is from " + listener.mOwnerUid); + } + return true; + } + + @VisibleForTesting + class WindowContextListenerImpl implements WindowContainerListener { + @NonNull private final IBinder mClientToken; + private final int mOwnerUid; + @NonNull private WindowContainer mContainer; + + private DeathRecipient mDeathRecipient; + + private int mLastReportedDisplay = INVALID_DISPLAY; + private Configuration mLastReportedConfig; + + private WindowContextListenerImpl(IBinder clientToken, WindowContainer container, + int ownerUid) { + mClientToken = clientToken; + mContainer = Objects.requireNonNull(container); + mOwnerUid = ownerUid; + + final DeathRecipient deathRecipient = new DeathRecipient(); + try { + deathRecipient.linkToDeath(); + mDeathRecipient = deathRecipient; + } catch (RemoteException e) { + ProtoLog.e(WM_ERROR, "Could not register window container listener token=%s, " + + "container=%s", mClientToken, mContainer); + } + } + + /** TEST ONLY: returns the {@link WindowContainer} of the listener */ + @VisibleForTesting + WindowContainer getWindowContainer() { + return mContainer; + } + + private void updateContainer(WindowContainer newContainer) { + if (mContainer.equals(newContainer)) { + return; + } + mContainer.unregisterWindowContainerListener(this); + mContainer = newContainer; + clear(); + register(); + } + + private void register() { + if (mDeathRecipient == null) { + throw new IllegalStateException("Invalid client token: " + mClientToken); + } + mListeners.putIfAbsent(mClientToken, this); + mContainer.registerWindowContainerListener(this); + reportConfigToWindowTokenClient(); + } + + private void unregister() { + mContainer.unregisterWindowContainerListener(this); + mListeners.remove(mClientToken); + } + + private void clear() { + mLastReportedConfig = null; + mLastReportedDisplay = INVALID_DISPLAY; + } + + @Override + public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { + reportConfigToWindowTokenClient(); + } + + @Override + public void onDisplayChanged(DisplayContent dc) { + reportConfigToWindowTokenClient(); + } + + private void reportConfigToWindowTokenClient() { + if (mDeathRecipient == null) { + throw new IllegalStateException("Invalid client token: " + mClientToken); + } + + if (mLastReportedConfig == null) { + mLastReportedConfig = new Configuration(); + } + final Configuration config = mContainer.getConfiguration(); + final int displayId = mContainer.getDisplayContent().getDisplayId(); + if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) { + // No changes since last reported time. + return; + } + + mLastReportedConfig.setTo(config); + mLastReportedDisplay = displayId; + + IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken); + try { + windowTokenClient.onConfigurationChanged(config, displayId); + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client."); + } + } + + @Override + public void onRemoved() { + if (mDeathRecipient == null) { + throw new IllegalStateException("Invalid client token: " + mClientToken); + } + mDeathRecipient.unlinkToDeath(); + IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken); + try { + windowTokenClient.onWindowTokenRemoved(); + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client."); + } + unregister(); + } + + private class DeathRecipient implements IBinder.DeathRecipient { + @Override + public void binderDied() { + synchronized (mContainer.mWmService.mGlobalLock) { + mDeathRecipient = null; + unregister(); + } + } + + void linkToDeath() throws RemoteException { + mClientToken.linkToDeath(this, 0); + } + + void unlinkToDeath() { + mClientToken.unlinkToDeath(this, 0); + } + } + } +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f8b5914d3e53..8fd342c25bc3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -44,8 +44,8 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; -import static android.provider.Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS; import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR; +import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; @@ -459,7 +459,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowTracing mWindowTracing; - final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider; + private final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider; final private KeyguardDisableHandler mKeyguardDisableHandler; // TODO: eventually unify all keyguard state in a common place instead of having it spread over @@ -769,6 +769,8 @@ public class WindowManagerService extends IWindowManager.Stub final AnrController mAnrController; private final ImpressionAttestationController mImpressionAttestationController; + private final WindowContextListenerController mWindowContextListenerController = + new WindowContextListenerController(); @VisibleForTesting final class SettingsObserver extends ContentObserver { @@ -796,8 +798,8 @@ public class WindowManagerService extends IWindowManager.Stub DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM); private final Uri mRenderShadowsInCompositorUri = Settings.Global.getUriFor( DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR); - private final Uri mIgnoreVendorDisplaySettingsUri = Settings.Global.getUriFor( - DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS); + private final Uri mDisplaySettingsPathUri = Settings.Global.getUriFor( + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); public SettingsObserver() { super(new Handler()); @@ -822,7 +824,7 @@ public class WindowManagerService extends IWindowManager.Stub UserHandle.USER_ALL); resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this, UserHandle.USER_ALL); - resolver.registerContentObserver(mIgnoreVendorDisplaySettingsUri, false, this, + resolver.registerContentObserver(mDisplaySettingsPathUri, false, this, UserHandle.USER_ALL); } @@ -867,8 +869,8 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (mIgnoreVendorDisplaySettingsUri.equals(uri)) { - updateIgnoreVendorDisplaySettings(); + if (mDisplaySettingsPathUri.equals(uri)) { + updateDisplaySettingsLocation(); return; } @@ -962,12 +964,12 @@ public class WindowManagerService extends IWindowManager.Stub mAtmService.mSizeCompatFreeform = sizeCompatFreeform; } - void updateIgnoreVendorDisplaySettings() { + void updateDisplaySettingsLocation() { final ContentResolver resolver = mContext.getContentResolver(); - final boolean ignoreVendorSettings = Settings.Global.getInt(resolver, - DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, 0) != 0; + final String filePath = Settings.Global.getString(resolver, + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); synchronized (mGlobalLock) { - mDisplayWindowSettingsProvider.setVendorSettingsIgnored(ignoreVendorSettings); + mDisplayWindowSettingsProvider.setBaseSettingsFilePath(filePath); mRoot.forAllDisplays(display -> { mDisplayWindowSettings.applySettingsToDisplayLocked(display); display.reconfigureDisplayLocked(); @@ -1121,10 +1123,7 @@ public class WindowManagerService extends IWindowManager.Stub final boolean isRecentsAnimationTarget = getRecentsAnimationController() != null && getRecentsAnimationController().isTargetApp(atoken); if (atoken.mLaunchTaskBehind && !isRecentsAnimationTarget) { - try { - mActivityTaskManager.notifyLaunchTaskBehindComplete(atoken.token); - } catch (RemoteException e) { - } + mAtmService.mTaskSupervisor.scheduleLaunchTaskBehindComplete(atoken.token); atoken.mLaunchTaskBehind = false; } else { atoken.updateReportedVisibilityLocked(); @@ -1132,9 +1131,11 @@ public class WindowManagerService extends IWindowManager.Stub // successfully finishes. if (atoken.mEnteringAnimation && !isRecentsAnimationTarget) { atoken.mEnteringAnimation = false; - try { - mActivityTaskManager.notifyEnterAnimationComplete(atoken.token); - } catch (RemoteException e) { + if (atoken != null && atoken.attachedToProcess()) { + try { + atoken.app.getThread().scheduleEnterAnimationComplete(atoken.appToken); + } catch (RemoteException e) { + } } } } @@ -1330,10 +1331,12 @@ public class WindowManagerService extends IWindowManager.Stub mForceDesktopModeOnExternalDisplays = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; - final boolean ignoreVendorDisplaySettings = Settings.Global.getInt(resolver, - DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, 0) != 0; + final String displaySettingsPath = Settings.Global.getString(resolver, + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); mDisplayWindowSettingsProvider = new DisplayWindowSettingsProvider(); - mDisplayWindowSettingsProvider.setVendorSettingsIgnored(ignoreVendorDisplaySettings); + if (displaySettingsPath != null) { + mDisplayWindowSettingsProvider.setBaseSettingsFilePath(displaySettingsPath); + } mDisplayWindowSettings = new DisplayWindowSettings(this, mDisplayWindowSettingsProvider); IntentFilter filter = new IntentFilter(); @@ -1376,6 +1379,10 @@ public class WindowManagerService extends IWindowManager.Stub mStartingSurfaceController = new StartingSurfaceController(this); } + DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() { + return mDisplayAreaPolicyProvider; + } + private void setGlobalShadowSettings() { final TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); float lightY = a.getDimension(R.styleable.Lighting_lightY, 0); @@ -2622,6 +2629,10 @@ public class WindowManagerService extends IWindowManager.Stub } boolean checkCallingPermission(String permission, String func) { + return checkCallingPermission(permission, func, true /* printLog */); + } + + boolean checkCallingPermission(String permission, String func, boolean printLog) { // Quick check: if the calling permission is me, it's all okay. if (Binder.getCallingPid() == myPid()) { return true; @@ -2631,8 +2642,10 @@ public class WindowManagerService extends IWindowManager.Stub == PackageManager.PERMISSION_GRANTED) { return true; } - ProtoLog.w(WM_ERROR, "Permission Denial: %s from pid=%d, uid=%d requires %s", - func, Binder.getCallingPid(), Binder.getCallingUid(), permission); + if (printLog) { + ProtoLog.w(WM_ERROR, "Permission Denial: %s from pid=%d, uid=%d requires %s", + func, Binder.getCallingPid(), Binder.getCallingUid(), permission); + } return false; } @@ -2725,6 +2738,52 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public boolean registerWindowContextListener(IBinder clientToken, int type, int displayId, + Bundle options) { + final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, + "registerWindowContextListener", false /* printLog */); + final int callingUid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId); + if (dc == null) { + ProtoLog.w(WM_ERROR, "registerWindowContextListener: trying to add listener to" + + " a non-existing display:%d", displayId); + return false; + } + // TODO(b/155340867): Investigate if we still need roundedCornerOverlay after + // the feature b/155340867 is completed. + final DisplayArea da = dc.getAreaForWindowToken(type, options, + callerCanManageAppTokens, false /* roundedCornerOverlay */); + mWindowContextListenerController.registerWindowContainerListener(clientToken, da, + callingUid); + return true; + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void unregisterWindowContextListener(IBinder clientToken) { + final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, + "unregisterWindowContextListener", false /* printLog */); + final int callingUid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + if (mWindowContextListenerController.assertCallerCanRemoveListener(clientToken, + callerCanManageAppTokens, callingUid)) { + mWindowContextListenerController.unregisterWindowContainerListener(clientToken); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override public boolean isWindowToken(IBinder binder) { synchronized (mGlobalLock) { final WindowToken windowToken = mRoot.getWindowToken(binder); @@ -5106,9 +5165,11 @@ public class WindowManagerService extends IWindowManager.Stub } case NOTIFY_ACTIVITY_DRAWN: { - try { - mActivityTaskManager.notifyActivityDrawn((IBinder) msg.obj); - } catch (RemoteException e) { + final ActivityRecord activity = (ActivityRecord) msg.obj; + synchronized (mGlobalLock) { + if (activity.isAttached()) { + activity.getRootTask().notifyActivityDrawnLocked(activity); + } } break; } @@ -7717,6 +7778,9 @@ public class WindowManagerService extends IWindowManager.Stub dc.mInputMethodControlTarget.hideInsets( WindowInsets.Type.ime(), true /* fromIme */); } + if (dc != null) { + dc.getInsetsStateController().getImeSourceProvider().setImeShowing(false); + } } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 5ced6a52050b..34875ed70889 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -419,6 +419,7 @@ class WindowToken extends WindowContainer<WindowState> { reportWindowTokenRemovedToClient(); } + // TODO(b/159767464): Remove after we migrate to listener approach. private void reportWindowTokenRemovedToClient() { if (!shouldReportToClient()) { return; diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 6da9517743d2..d37c7b03dfb5 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -152,7 +152,7 @@ cc_defaults { "android.hardware.power.stats@1.0", "android.hardware.thermal@1.0", "android.hardware.tv.input@1.0", - "android.hardware.vibrator-cpp", + "android.hardware.vibrator-unstable-cpp", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 9e0fb567975a..13450be73d88 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -238,16 +238,16 @@ public: /* --- InputReaderPolicyInterface implementation --- */ - virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); - virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId); - virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices); - virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( - const InputDeviceIdentifier& identifier); - virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier); - virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env, - jfloatArray matrixArr); - virtual TouchAffineTransformation getTouchAffineTransformation( - const std::string& inputDeviceDescriptor, int32_t surfaceRotation); + void getReaderConfiguration(InputReaderConfiguration* outConfig) override; + std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override; + void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override; + std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( + const InputDeviceIdentifier& identifier) override; + std::string getDeviceAlias(const InputDeviceIdentifier& identifier) override; + TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, + int32_t surfaceRotation) override; + + TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr); /* --- InputDispatcherPolicyInterface implementation --- */ diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd index 0d8c08c93ff2..501450398fc3 100644 --- a/services/core/xsd/device-state-config/device-state-config.xsd +++ b/services/core/xsd/device-state-config/device-state-config.xsd @@ -57,8 +57,8 @@ <xs:complexType name="sensorCondition"> <xs:sequence> + <xs:element name="type" type="xs:string" /> <xs:element name="name" type="xs:string" /> - <xs:element name="type" type="xs:positiveInteger" /> <xs:element name="value" type="numericRange" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt index 667d1add5a98..b396af0fa0c6 100644 --- a/services/core/xsd/device-state-config/schema/current.txt +++ b/services/core/xsd/device-state-config/schema/current.txt @@ -44,10 +44,10 @@ package com.android.server.policy.devicestate.config { public class SensorCondition { ctor public SensorCondition(); method public String getName(); - method public java.math.BigInteger getType(); + method public String getType(); method public java.util.List<com.android.server.policy.devicestate.config.NumericRange> getValue(); method public void setName(String); - method public void setType(java.math.BigInteger); + method public void setType(String); } public class XmlParser { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index 1a147b9f73e4..a281180d77d1 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -40,14 +40,14 @@ import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.pm.UserRestrictionsUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -300,7 +300,7 @@ class ActiveAdmin { return UserHandle.of(UserHandle.getUserId(info.getActivityInfo().applicationInfo.uid)); } - void writeToXml(XmlSerializer out) + void writeToXml(TypedXmlSerializer out) throws IllegalArgumentException, IllegalStateException, IOException { out.startTag(null, TAG_POLICIES); info.writePoliciesToXml(out); @@ -413,11 +413,11 @@ class ActiveAdmin { } if (isNetworkLoggingEnabled) { out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED); - out.attribute(null, ATTR_VALUE, Boolean.toString(isNetworkLoggingEnabled)); - out.attribute(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS, - Integer.toString(numNetworkLoggingNotifications)); - out.attribute(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION, - Long.toString(lastNetworkLoggingNotificationTimeMs)); + out.attributeBoolean(null, ATTR_VALUE, isNetworkLoggingEnabled); + out.attributeInt(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS, + numNetworkLoggingNotifications); + out.attributeLong(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION, + lastNetworkLoggingNotificationTimeMs); out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED); } if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) { @@ -535,13 +535,13 @@ class ActiveAdmin { } } - void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException { + void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException { out.startTag(null, tag); out.text(text); out.endTag(null, tag); } - void writePackageListToXml(XmlSerializer out, String outerTag, + void writePackageListToXml(TypedXmlSerializer out, String outerTag, List<String> packageList) throws IllegalArgumentException, IllegalStateException, IOException { if (packageList == null) { @@ -550,35 +550,35 @@ class ActiveAdmin { writeAttributeValuesToXml(out, outerTag, TAG_PACKAGE_LIST_ITEM, packageList); } - void writeAttributeValueToXml(XmlSerializer out, String tag, String value) + void writeAttributeValueToXml(TypedXmlSerializer out, String tag, String value) throws IOException { out.startTag(null, tag); out.attribute(null, ATTR_VALUE, value); out.endTag(null, tag); } - void writeAttributeValueToXml(XmlSerializer out, String tag, int value) + void writeAttributeValueToXml(TypedXmlSerializer out, String tag, int value) throws IOException { out.startTag(null, tag); - out.attribute(null, ATTR_VALUE, Integer.toString(value)); + out.attributeInt(null, ATTR_VALUE, value); out.endTag(null, tag); } - void writeAttributeValueToXml(XmlSerializer out, String tag, long value) + void writeAttributeValueToXml(TypedXmlSerializer out, String tag, long value) throws IOException { out.startTag(null, tag); - out.attribute(null, ATTR_VALUE, Long.toString(value)); + out.attributeLong(null, ATTR_VALUE, value); out.endTag(null, tag); } - void writeAttributeValueToXml(XmlSerializer out, String tag, boolean value) + void writeAttributeValueToXml(TypedXmlSerializer out, String tag, boolean value) throws IOException { out.startTag(null, tag); - out.attribute(null, ATTR_VALUE, Boolean.toString(value)); + out.attributeBoolean(null, ATTR_VALUE, value); out.endTag(null, tag); } - void writeAttributeValuesToXml(XmlSerializer out, String outerTag, String innerTag, + void writeAttributeValuesToXml(TypedXmlSerializer out, String outerTag, String innerTag, @NonNull Collection<String> values) throws IOException { out.startTag(null, outerTag); for (String value : values) { @@ -589,7 +589,7 @@ class ActiveAdmin { out.endTag(null, outerTag); } - void readFromXml(XmlPullParser parser, boolean shouldOverridePolicies) + void readFromXml(TypedXmlPullParser parser, boolean shouldOverridePolicies) throws XmlPullParserException, IOException { int outerDepth = parser.getDepth(); int type; @@ -606,47 +606,33 @@ class ActiveAdmin { info.readPoliciesFromXml(parser); } } else if (TAG_PASSWORD_QUALITY.equals(tag)) { - mPasswordPolicy.quality = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.quality = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_LENGTH.equals(tag)) { - mPasswordPolicy.length = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.length = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_PASSWORD_HISTORY_LENGTH.equals(tag)) { - passwordHistoryLength = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + passwordHistoryLength = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_UPPERCASE.equals(tag)) { - mPasswordPolicy.upperCase = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.upperCase = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_LOWERCASE.equals(tag)) { - mPasswordPolicy.lowerCase = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.lowerCase = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_LETTERS.equals(tag)) { - mPasswordPolicy.letters = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.letters = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_NUMERIC.equals(tag)) { - mPasswordPolicy.numeric = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.numeric = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_SYMBOLS.equals(tag)) { - mPasswordPolicy.symbols = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.symbols = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) { - mPasswordPolicy.nonLetter = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.nonLetter = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT.equals(tag)) { - mPasswordPolicyAppliesToParent = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicyAppliesToParent = parser.getAttributeBoolean(null, ATTR_VALUE); } else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) { - maximumTimeToUnlock = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + maximumTimeToUnlock = parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_STRONG_AUTH_UNLOCK_TIMEOUT.equals(tag)) { - strongAuthUnlockTimeout = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + strongAuthUnlockTimeout = parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_MAX_FAILED_PASSWORD_WIPE.equals(tag)) { - maximumFailedPasswordsForWipe = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + maximumFailedPasswordsForWipe = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_SPECIFIES_GLOBAL_PROXY.equals(tag)) { - specifiesGlobalProxy = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + specifiesGlobalProxy = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_GLOBAL_PROXY_SPEC.equals(tag)) { globalProxySpec = parser.getAttributeValue(null, ATTR_VALUE); @@ -654,48 +640,36 @@ class ActiveAdmin { globalProxyExclusionList = parser.getAttributeValue(null, ATTR_VALUE); } else if (TAG_PASSWORD_EXPIRATION_TIMEOUT.equals(tag)) { - passwordExpirationTimeout = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + passwordExpirationTimeout = parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_PASSWORD_EXPIRATION_DATE.equals(tag)) { - passwordExpirationDate = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + passwordExpirationDate = parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_ENCRYPTION_REQUESTED.equals(tag)) { - encryptionRequested = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + encryptionRequested = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_TEST_ONLY_ADMIN.equals(tag)) { - testOnlyAdmin = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + testOnlyAdmin = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_DISABLE_CAMERA.equals(tag)) { - disableCamera = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + disableCamera = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_DISABLE_CALLER_ID.equals(tag)) { - disableCallerId = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + disableCallerId = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_DISABLE_CONTACTS_SEARCH.equals(tag)) { - disableContactsSearch = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + disableContactsSearch = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_DISABLE_BLUETOOTH_CONTACT_SHARING.equals(tag)) { - disableBluetoothContactSharing = Boolean.parseBoolean(parser - .getAttributeValue(null, ATTR_VALUE)); + disableBluetoothContactSharing = + parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_DISABLE_SCREEN_CAPTURE.equals(tag)) { - disableScreenCapture = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + disableScreenCapture = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_REQUIRE_AUTO_TIME.equals(tag)) { - requireAutoTime = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + requireAutoTime = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_FORCE_EPHEMERAL_USERS.equals(tag)) { - forceEphemeralUsers = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + forceEphemeralUsers = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_IS_NETWORK_LOGGING_ENABLED.equals(tag)) { - isNetworkLoggingEnabled = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); - lastNetworkLoggingNotificationTimeMs = Long.parseLong( - parser.getAttributeValue(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION)); - numNetworkLoggingNotifications = Integer.parseInt( - parser.getAttributeValue(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS)); + isNetworkLoggingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false); + lastNetworkLoggingNotificationTimeMs = parser.getAttributeLong(null, + ATTR_LAST_NETWORK_LOGGING_NOTIFICATION); + numNetworkLoggingNotifications = parser.getAttributeInt(null, + ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS); } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) { - disabledKeyguardFeatures = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + disabledKeyguardFeatures = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) { readAttributeValues( parser, TAG_ACCOUNT_TYPE, accountTypesWithManagementDisabled); @@ -721,7 +695,7 @@ class ActiveAdmin { parser, TAG_RESTRICTION, defaultEnabledRestrictionsAlreadySet); } else if (TAG_SHORT_SUPPORT_MESSAGE.equals(tag)) { type = parser.next(); - if (type == XmlPullParser.TEXT) { + if (type == TypedXmlPullParser.TEXT) { shortSupportMessage = parser.getText(); } else { Log.w(DevicePolicyManagerService.LOG_TAG, @@ -729,7 +703,7 @@ class ActiveAdmin { } } else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) { type = parser.next(); - if (type == XmlPullParser.TEXT) { + if (type == TypedXmlPullParser.TEXT) { longSupportMessage = parser.getText(); } else { Log.w(DevicePolicyManagerService.LOG_TAG, @@ -740,22 +714,20 @@ class ActiveAdmin { parentAdmin = new ActiveAdmin(info, /* parent */ true); parentAdmin.readFromXml(parser, shouldOverridePolicies); } else if (TAG_ORGANIZATION_COLOR.equals(tag)) { - organizationColor = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + organizationColor = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_ORGANIZATION_NAME.equals(tag)) { type = parser.next(); - if (type == XmlPullParser.TEXT) { + if (type == TypedXmlPullParser.TEXT) { organizationName = parser.getText(); } else { Log.w(DevicePolicyManagerService.LOG_TAG, "Missing text when loading organization name"); } } else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) { - isLogoutEnabled = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + isLogoutEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_START_USER_SESSION_MESSAGE.equals(tag)) { type = parser.next(); - if (type == XmlPullParser.TEXT) { + if (type == TypedXmlPullParser.TEXT) { startUserSessionMessage = parser.getText(); } else { Log.w(DevicePolicyManagerService.LOG_TAG, @@ -763,7 +735,7 @@ class ActiveAdmin { } } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) { type = parser.next(); - if (type == XmlPullParser.TEXT) { + if (type == TypedXmlPullParser.TEXT) { endUserSessionMessage = parser.getText(); } else { Log.w(DevicePolicyManagerService.LOG_TAG, @@ -779,24 +751,21 @@ class ActiveAdmin { mFactoryResetProtectionPolicy = FactoryResetProtectionPolicy.readFromXml( parser); } else if (TAG_SUSPEND_PERSONAL_APPS.equals(tag)) { - mSuspendPersonalApps = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + mSuspendPersonalApps = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_PROFILE_MAXIMUM_TIME_OFF.equals(tag)) { mProfileMaximumTimeOffMillis = - Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE)); + parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) { mProfileOffDeadline = - Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE)); + parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_ALWAYS_ON_VPN_PACKAGE.equals(tag)) { mAlwaysOnVpnPackage = parser.getAttributeValue(null, ATTR_VALUE); } else if (TAG_ALWAYS_ON_VPN_LOCKDOWN.equals(tag)) { - mAlwaysOnVpnLockdown = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + mAlwaysOnVpnLockdown = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_COMMON_CRITERIA_MODE.equals(tag)) { - mCommonCriteriaMode = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + mCommonCriteriaMode = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_PASSWORD_COMPLEXITY.equals(tag)) { - mPasswordComplexity = Integer.parseInt(parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordComplexity = parser.getAttributeInt(null, ATTR_VALUE); } else { Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -804,14 +773,14 @@ class ActiveAdmin { } } - private List<String> readPackageList(XmlPullParser parser, + private List<String> readPackageList(TypedXmlPullParser parser, String tag) throws XmlPullParserException, IOException { List<String> result = new ArrayList<String>(); int outerDepth = parser.getDepth(); int outerType; - while ((outerType = parser.next()) != XmlPullParser.END_DOCUMENT - && (outerType != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (outerType == XmlPullParser.END_TAG || outerType == XmlPullParser.TEXT) { + while ((outerType = parser.next()) != TypedXmlPullParser.END_DOCUMENT + && (outerType != TypedXmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (outerType == TypedXmlPullParser.END_TAG || outerType == TypedXmlPullParser.TEXT) { continue; } String outerTag = parser.getName(); @@ -832,7 +801,7 @@ class ActiveAdmin { } private void readAttributeValues( - XmlPullParser parser, String tag, Collection<String> result) + TypedXmlPullParser parser, String tag, Collection<String> result) throws XmlPullParserException, IOException { result.clear(); int outerDepthDAM = parser.getDepth(); @@ -854,7 +823,7 @@ class ActiveAdmin { @NonNull private ArrayMap<String, TrustAgentInfo> getAllTrustAgentInfos( - XmlPullParser parser, String tag) throws XmlPullParserException, IOException { + TypedXmlPullParser parser, String tag) throws XmlPullParserException, IOException { int outerDepthDAM = parser.getDepth(); int typeDAM; final ArrayMap<String, TrustAgentInfo> result = new ArrayMap<>(); @@ -876,7 +845,7 @@ class ActiveAdmin { return result; } - private TrustAgentInfo getTrustAgentInfo(XmlPullParser parser, String tag) + private TrustAgentInfo getTrustAgentInfo(TypedXmlPullParser parser, String tag) throws XmlPullParserException, IOException { int outerDepthDAM = parser.getDepth(); int typeDAM; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java index 8a585ec62d00..31ba1991ee72 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java @@ -170,24 +170,19 @@ class DevicePolicyData { } if (policyData.mUserSetupComplete) { if (VERBOSE_LOG) Slog.v(TAG, "setting " + ATTR_SETUP_COMPLETE + " to true"); - out.attribute(null, ATTR_SETUP_COMPLETE, - Boolean.toString(true)); + out.attributeBoolean(null, ATTR_SETUP_COMPLETE, true); } if (policyData.mPaired) { - out.attribute(null, ATTR_DEVICE_PAIRED, - Boolean.toString(true)); + out.attributeBoolean(null, ATTR_DEVICE_PAIRED, true); } if (policyData.mDeviceProvisioningConfigApplied) { - out.attribute(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED, - Boolean.toString(true)); + out.attributeBoolean(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED, true); } if (policyData.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) { - out.attribute(null, ATTR_PROVISIONING_STATE, - Integer.toString(policyData.mUserProvisioningState)); + out.attributeInt(null, ATTR_PROVISIONING_STATE, policyData.mUserProvisioningState); } if (policyData.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) { - out.attribute(null, ATTR_PERMISSION_POLICY, - Integer.toString(policyData.mPermissionPolicy)); + out.attributeInt(null, ATTR_PERMISSION_POLICY, policyData.mPermissionPolicy); } // Serialize delegations. @@ -217,13 +212,13 @@ class DevicePolicyData { if (policyData.mPasswordOwner >= 0) { out.startTag(null, "password-owner"); - out.attribute(null, "value", Integer.toString(policyData.mPasswordOwner)); + out.attributeInt(null, "value", policyData.mPasswordOwner); out.endTag(null, "password-owner"); } if (policyData.mFailedPasswordAttempts != 0) { out.startTag(null, "failed-password-attempts"); - out.attribute(null, "value", Integer.toString(policyData.mFailedPasswordAttempts)); + out.attributeInt(null, "value", policyData.mFailedPasswordAttempts); out.endTag(null, "failed-password-attempts"); } @@ -232,8 +227,7 @@ class DevicePolicyData { // security reasons, we don't want to store the full set of active password metrics. if (isFdeDevice) { out.startTag(null, TAG_PASSWORD_VALIDITY); - out.attribute(null, ATTR_VALUE, - Boolean.toString(policyData.mPasswordValidAtLastCheckpoint)); + out.attributeBoolean(null, ATTR_VALUE, policyData.mPasswordValidAtLastCheckpoint); out.endTag(null, TAG_PASSWORD_VALIDITY); } @@ -252,19 +246,19 @@ class DevicePolicyData { if (policyData.mLockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE) { out.startTag(null, TAG_LOCK_TASK_FEATURES); - out.attribute(null, ATTR_VALUE, Integer.toString(policyData.mLockTaskFeatures)); + out.attributeInt(null, ATTR_VALUE, policyData.mLockTaskFeatures); out.endTag(null, TAG_LOCK_TASK_FEATURES); } if (policyData.mSecondaryLockscreenEnabled) { out.startTag(null, TAG_SECONDARY_LOCK_SCREEN); - out.attribute(null, ATTR_VALUE, Boolean.toString(true)); + out.attributeBoolean(null, ATTR_VALUE, true); out.endTag(null, TAG_SECONDARY_LOCK_SCREEN); } if (policyData.mStatusBarDisabled) { out.startTag(null, TAG_STATUS_BAR); - out.attribute(null, ATTR_DISABLED, Boolean.toString(policyData.mStatusBarDisabled)); + out.attributeBoolean(null, ATTR_DISABLED, policyData.mStatusBarDisabled); out.endTag(null, TAG_STATUS_BAR); } @@ -281,29 +275,25 @@ class DevicePolicyData { if (policyData.mLastSecurityLogRetrievalTime >= 0) { out.startTag(null, TAG_LAST_SECURITY_LOG_RETRIEVAL); - out.attribute(null, ATTR_VALUE, - Long.toString(policyData.mLastSecurityLogRetrievalTime)); + out.attributeLong(null, ATTR_VALUE, policyData.mLastSecurityLogRetrievalTime); out.endTag(null, TAG_LAST_SECURITY_LOG_RETRIEVAL); } if (policyData.mLastBugReportRequestTime >= 0) { out.startTag(null, TAG_LAST_BUG_REPORT_REQUEST); - out.attribute(null, ATTR_VALUE, - Long.toString(policyData.mLastBugReportRequestTime)); + out.attributeLong(null, ATTR_VALUE, policyData.mLastBugReportRequestTime); out.endTag(null, TAG_LAST_BUG_REPORT_REQUEST); } if (policyData.mLastNetworkLogsRetrievalTime >= 0) { out.startTag(null, TAG_LAST_NETWORK_LOG_RETRIEVAL); - out.attribute(null, ATTR_VALUE, - Long.toString(policyData.mLastNetworkLogsRetrievalTime)); + out.attributeLong(null, ATTR_VALUE, policyData.mLastNetworkLogsRetrievalTime); out.endTag(null, TAG_LAST_NETWORK_LOG_RETRIEVAL); } if (policyData.mAdminBroadcastPending) { out.startTag(null, TAG_ADMIN_BROADCAST_PENDING); - out.attribute(null, ATTR_VALUE, - Boolean.toString(policyData.mAdminBroadcastPending)); + out.attributeBoolean(null, ATTR_VALUE, policyData.mAdminBroadcastPending); out.endTag(null, TAG_ADMIN_BROADCAST_PENDING); } @@ -315,8 +305,7 @@ class DevicePolicyData { if (policyData.mPasswordTokenHandle != 0) { out.startTag(null, TAG_PASSWORD_TOKEN_HANDLE); - out.attribute(null, ATTR_VALUE, - Long.toString(policyData.mPasswordTokenHandle)); + out.attributeLong(null, ATTR_VALUE, policyData.mPasswordTokenHandle); out.endTag(null, TAG_PASSWORD_TOKEN_HANDLE); } @@ -340,7 +329,7 @@ class DevicePolicyData { if (policyData.mAppsSuspended) { out.startTag(null, TAG_APPS_SUSPENDED); - out.attribute(null, ATTR_VALUE, Boolean.toString(policyData.mAppsSuspended)); + out.attributeBoolean(null, ATTR_VALUE, policyData.mAppsSuspended); out.endTag(null, TAG_APPS_SUSPENDED); } @@ -413,13 +402,13 @@ class DevicePolicyData { if (Boolean.toString(true).equals(deviceProvisioningConfigApplied)) { policy.mDeviceProvisioningConfigApplied = true; } - String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE); - if (!TextUtils.isEmpty(provisioningState)) { - policy.mUserProvisioningState = Integer.parseInt(provisioningState); + int provisioningState = parser.getAttributeInt(null, ATTR_PROVISIONING_STATE, -1); + if (provisioningState != -1) { + policy.mUserProvisioningState = provisioningState; } - String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY); - if (!TextUtils.isEmpty(permissionPolicy)) { - policy.mPermissionPolicy = Integer.parseInt(permissionPolicy); + int permissionPolicy = parser.getAttributeInt(null, ATTR_PERMISSION_POLICY, -1); + if (permissionPolicy != -1) { + policy.mPermissionPolicy = permissionPolicy; } parser.next(); @@ -472,37 +461,34 @@ class DevicePolicyData { scopes.add(scope); } } else if ("failed-password-attempts".equals(tag)) { - policy.mFailedPasswordAttempts = Integer.parseInt( - parser.getAttributeValue(null, "value")); + policy.mFailedPasswordAttempts = parser.getAttributeInt(null, "value"); } else if ("password-owner".equals(tag)) { - policy.mPasswordOwner = Integer.parseInt( - parser.getAttributeValue(null, "value")); + policy.mPasswordOwner = parser.getAttributeInt(null, "value"); } else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) { policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME)); } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) { policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name")); } else if (TAG_LOCK_TASK_FEATURES.equals(tag)) { - policy.mLockTaskFeatures = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mLockTaskFeatures = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_SECONDARY_LOCK_SCREEN.equals(tag)) { - policy.mSecondaryLockscreenEnabled = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mSecondaryLockscreenEnabled = + parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_STATUS_BAR.equals(tag)) { - policy.mStatusBarDisabled = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_DISABLED)); + policy.mStatusBarDisabled = + parser.getAttributeBoolean(null, ATTR_DISABLED, false); } else if (TAG_DO_NOT_ASK_CREDENTIALS_ON_BOOT.equals(tag)) { policy.mDoNotAskCredentialsOnBoot = true; } else if (TAG_AFFILIATION_ID.equals(tag)) { policy.mAffiliationIds.add(parser.getAttributeValue(null, ATTR_ID)); } else if (TAG_LAST_SECURITY_LOG_RETRIEVAL.equals(tag)) { - policy.mLastSecurityLogRetrievalTime = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mLastSecurityLogRetrievalTime = + parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_LAST_BUG_REPORT_REQUEST.equals(tag)) { - policy.mLastBugReportRequestTime = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mLastBugReportRequestTime = + parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_LAST_NETWORK_LOG_RETRIEVAL.equals(tag)) { - policy.mLastNetworkLogsRetrievalTime = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mLastNetworkLogsRetrievalTime = + parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_ADMIN_BROADCAST_PENDING.equals(tag)) { String pending = parser.getAttributeValue(null, ATTR_VALUE); policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending); @@ -515,12 +501,11 @@ class DevicePolicyData { } else if (TAG_PASSWORD_VALIDITY.equals(tag)) { if (isFdeDevice) { // This flag is only used for FDE devices - policy.mPasswordValidAtLastCheckpoint = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mPasswordValidAtLastCheckpoint = + parser.getAttributeBoolean(null, ATTR_VALUE, false); } } else if (TAG_PASSWORD_TOKEN_HANDLE.equals(tag)) { - policy.mPasswordTokenHandle = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mPasswordTokenHandle = parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_CURRENT_INPUT_METHOD_SET.equals(tag)) { policy.mCurrentInputMethodSet = true; } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) { @@ -530,7 +515,7 @@ class DevicePolicyData { parser.getAttributeValue(null, ATTR_NAME)); } else if (TAG_APPS_SUSPENDED.equals(tag)) { policy.mAppsSuspended = - Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_VALUE)); + parser.getAttributeBoolean(null, ATTR_VALUE, false); } else { Slog.w(TAG, "Unknown tag: " + tag); XmlUtils.skipCurrentTag(parser); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index bed450a196d9..edcf9e8399a8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4311,11 +4311,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { updatePasswordQualityCacheForUserGroup(caller.getUserId()); saveSettingsLocked(caller.getUserId()); }); + + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.SET_PASSWORD_COMPLEXITY) + .setAdmin(admin.info.getPackageName()) + .setInt(passwordComplexity) + .setBoolean(calledOnParent) + .write(); } logPasswordComplexityRequiredIfSecurityLogEnabled(admin.info.getComponent(), caller.getUserId(), calledOnParent, passwordComplexity); } - //TODO: Log metrics. } private void logPasswordComplexityRequiredIfSecurityLogEnabled(ComponentName who, int userId, @@ -5862,7 +5868,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Should include alias in authentication policy try (KeyChainConnection connection = KeyChain.bindAsUser(mContext, caller.getUserHandle())) { - if (!containsAlias(connection.getService().getCredentialManagementAppPolicy(), alias)) { + // The policy will be null if there is no credential management app + AppUriAuthenticationPolicy policy = + connection.getService().getCredentialManagementAppPolicy(); + if (policy == null || policy.getAppAndUriMappings().isEmpty() + || !containsAlias(policy, alias)) { return false; } } catch (RemoteException | InterruptedException e) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 8ef69822750f..79a82b8e7175 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -43,22 +43,18 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import libcore.io.IoUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.util.List; import java.util.Map; @@ -558,11 +554,10 @@ class Owners { } try { InputStream input = new AtomicFile(file).openRead(); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(input, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(input); int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT) { - if (type!=XmlPullParser.START_TAG) { + while ((type=parser.next()) != TypedXmlPullParser.END_DOCUMENT) { + if (type!=TypedXmlPullParser.START_TAG) { continue; } @@ -581,7 +576,7 @@ class Owners { String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME); String profileOwnerComponentStr = parser.getAttributeValue(null, ATTR_COMPONENT_NAME); - int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USERID)); + int userId = parser.getAttributeInt(null, ATTR_USERID); OwnerInfo profileOwnerInfo = null; if (profileOwnerComponentStr != null) { ComponentName admin = ComponentName.unflattenFromString( @@ -781,12 +776,12 @@ class Owners { int type; int depth = 0; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + while ((type = parser.next()) != TypedXmlPullParser.END_DOCUMENT) { switch (type) { - case XmlPullParser.START_TAG: + case TypedXmlPullParser.START_TAG: depth++; break; - case XmlPullParser.END_TAG: + case TypedXmlPullParser.END_TAG: depth--; // fallthrough default: @@ -813,9 +808,9 @@ class Owners { } } - abstract void writeInner(XmlSerializer out) throws IOException; + abstract void writeInner(TypedXmlSerializer out) throws IOException; - abstract boolean readInner(XmlPullParser parser, int depth, String tag); + abstract boolean readInner(TypedXmlPullParser parser, int depth, String tag); } private class DeviceOwnerReadWriter extends FileReadWriter { @@ -831,11 +826,11 @@ class Owners { } @Override - void writeInner(XmlSerializer out) throws IOException { + void writeInner(TypedXmlSerializer out) throws IOException { if (mDeviceOwner != null) { mDeviceOwner.writeToXml(out, TAG_DEVICE_OWNER); out.startTag(null, TAG_DEVICE_OWNER_CONTEXT); - out.attribute(null, ATTR_USERID, String.valueOf(mDeviceOwnerUserId)); + out.attributeInt(null, ATTR_USERID, mDeviceOwnerUserId); out.endTag(null, TAG_DEVICE_OWNER_CONTEXT); } @@ -863,7 +858,7 @@ class Owners { } @Override - boolean readInner(XmlPullParser parser, int depth, String tag) { + boolean readInner(TypedXmlPullParser parser, int depth, String tag) { if (depth > 2) { return true; // Ignore } @@ -873,13 +868,8 @@ class Owners { mDeviceOwnerUserId = UserHandle.USER_SYSTEM; // Set default break; case TAG_DEVICE_OWNER_CONTEXT: { - final String userIdString = - parser.getAttributeValue(null, ATTR_USERID); - try { - mDeviceOwnerUserId = Integer.parseInt(userIdString); - } catch (NumberFormatException e) { - Slog.e(TAG, "Error parsing user-id " + userIdString); - } + mDeviceOwnerUserId = parser.getAttributeInt(null, ATTR_USERID, + mDeviceOwnerUserId); break; } case TAG_DEVICE_INITIALIZER: @@ -927,7 +917,7 @@ class Owners { } @Override - void writeInner(XmlSerializer out) throws IOException { + void writeInner(TypedXmlSerializer out) throws IOException { final OwnerInfo profileOwner = mProfileOwners.get(mUserId); if (profileOwner != null) { profileOwner.writeToXml(out, TAG_PROFILE_OWNER); @@ -935,7 +925,7 @@ class Owners { } @Override - boolean readInner(XmlPullParser parser, int depth, String tag) { + boolean readInner(TypedXmlPullParser parser, int depth, String tag) { if (depth > 2) { return true; // Ignore } @@ -985,7 +975,7 @@ class Owners { this.isOrganizationOwnedDevice = isOrganizationOwnedDevice; } - public void writeToXml(XmlSerializer out, String tag) throws IOException { + public void writeToXml(TypedXmlSerializer out, String tag) throws IOException { out.startTag(null, tag); out.attribute(null, ATTR_PACKAGE, packageName); if (name != null) { @@ -994,8 +984,7 @@ class Owners { if (admin != null) { out.attribute(null, ATTR_COMPONENT_NAME, admin.flattenToString()); } - out.attribute(null, ATTR_USER_RESTRICTIONS_MIGRATED, - String.valueOf(userRestrictionsMigrated)); + out.attributeBoolean(null, ATTR_USER_RESTRICTIONS_MIGRATED, userRestrictionsMigrated); if (remoteBugreportUri != null) { out.attribute(null, ATTR_REMOTE_BUGREPORT_URI, remoteBugreportUri); } @@ -1003,13 +992,13 @@ class Owners { out.attribute(null, ATTR_REMOTE_BUGREPORT_HASH, remoteBugreportHash); } if (isOrganizationOwnedDevice) { - out.attribute(null, ATTR_PROFILE_OWNER_OF_ORG_OWNED_DEVICE, - String.valueOf(isOrganizationOwnedDevice)); + out.attributeBoolean(null, ATTR_PROFILE_OWNER_OF_ORG_OWNED_DEVICE, + isOrganizationOwnedDevice); } out.endTag(null, tag); } - public static OwnerInfo readFromXml(XmlPullParser parser) { + public static OwnerInfo readFromXml(TypedXmlPullParser parser) { final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); final String name = parser.getAttributeValue(null, ATTR_NAME); final String componentName = diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java index 58ece0748d8b..289ed364e2a3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java @@ -29,18 +29,15 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.Objects; /** @@ -106,7 +103,7 @@ class TransferOwnershipMetadataManager { return false; } - private void insertSimpleTag(XmlSerializer serializer, String tagName, String value) + private void insertSimpleTag(TypedXmlSerializer serializer, String tagName, String value) throws IOException { serializer.startTag(null, tagName); serializer.text(value); @@ -132,7 +129,7 @@ class TransferOwnershipMetadataManager { return null; } - private Metadata parseMetadataFile(XmlPullParser parser) + private Metadata parseMetadataFile(TypedXmlPullParser parser) throws XmlPullParserException, IOException { int type; final int outerDepth = parser.getDepth(); 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 3e06194f8bee..b5e595a42ca9 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -554,7 +554,7 @@ public class DataManager { @Nullable List<String> shortcutIds) { @ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER - | ShortcutQuery.FLAG_MATCH_CACHED; + | ShortcutQuery.FLAG_MATCH_CACHED | ShortcutQuery.FLAG_GET_PERSONS_DATA; return mShortcutServiceInternal.getShortcuts( UserHandle.USER_SYSTEM, mContext.getPackageName(), /*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null, diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 8fc5c085999e..03083c1f00b2 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -15,7 +15,7 @@ android_test { name: "FrameworksMockingServicesTests", - srcs: ["src/**/*.java"], + srcs: ["src/**/*.java", "src/**/*.kt"], static_libs: [ "services.core", @@ -29,6 +29,10 @@ android_test { "mockito-target-extended-minus-junit4", "platform-test-annotations", "truth-prebuilt", + "hamcrest-library", + "servicestests-utils-mockito-extended", + "mockingservicestests-utils-mockito", + "servicestests-core-utils", "testables", // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows "testng", @@ -54,3 +58,17 @@ android_test { enabled: false, }, } + +java_library { + name: "mockingservicestests-utils-mockito", + srcs: [ + "utils-mockito/**/*.kt", + ], + static_libs: [ + "junit", + "mockito-target-extended-minus-junit4", + ], + libs: [ + "android.test.runner", + ], +}
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 7f86faa4b393..78bcc13c9265 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -145,7 +145,7 @@ public class ConnectivityControllerTest { final ConnectivityController controller = new ConnectivityController(mService); when(mService.getMaxJobExecutionTimeMs(any())) - .thenReturn(JobServiceContext.EXECUTING_TIMESLICE_MILLIS); + .thenReturn(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS); // Slow network is too slow assertFalse(controller.isSatisfied(createJobStatus(job), net, diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 8795d7711c63..4f3564d70416 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -35,6 +35,7 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -42,6 +43,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; @@ -76,6 +78,7 @@ import android.util.SparseBooleanArray; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; @@ -269,8 +272,9 @@ public class QuotaControllerTest { verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)).delete(eq(uid)); assertFalse(foregroundUids.get(uid)); } - } catch (RemoteException e) { - fail("registerUidObserver threw exception: " + e.getMessage()); + waitForQuietBackground(); + } catch (Exception e) { + fail("exception encountered: " + e.getMessage()); } } @@ -323,6 +327,14 @@ public class QuotaControllerTest { return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo); } + private JobStatus createHpjJobStatus(String testTag, int jobId) { + JobInfo jobInfo = new JobInfo.Builder(jobId, + new ComponentName(mContext, "TestQuotaHpjJobService")) + .setForeground(true) + .build(); + return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo); + } + private JobStatus createJobStatus(String testTag, String packageName, int callingUid, JobInfo jobInfo) { JobStatus js = JobStatus.createFromJobInfo( @@ -356,26 +368,49 @@ public class QuotaControllerTest { } } + private void waitForQuietBackground() throws Exception { + for (int i = 0; i < 5; ++i) { + if (!mQuotaController.isActiveBackgroundProcessing()) { + break; + } + Thread.sleep(500); + } + } + @Test public void testSaveTimingSession() { assertNull(mQuotaController.getTimingSessions(0, "com.android.test")); - List<TimingSession> expected = new ArrayList<>(); + List<TimingSession> expectedRegular = new ArrayList<>(); + List<TimingSession> expectedHpj = new ArrayList<>(); TimingSession one = new TimingSession(1, 10, 1); TimingSession two = new TimingSession(11, 20, 2); TimingSession thr = new TimingSession(21, 30, 3); - - mQuotaController.saveTimingSession(0, "com.android.test", one); - expected.add(one); - assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test")); - - mQuotaController.saveTimingSession(0, "com.android.test", two); - expected.add(two); - assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test")); - - mQuotaController.saveTimingSession(0, "com.android.test", thr); - expected.add(thr); - assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test")); + TimingSession fou = new TimingSession(31, 40, 4); + + mQuotaController.saveTimingSession(0, "com.android.test", one, false); + expectedRegular.add(one); + assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test")); + assertTrue( + ArrayUtils.isEmpty(mQuotaController.getHpjTimingSessions(0, "com.android.test"))); + + mQuotaController.saveTimingSession(0, "com.android.test", two, false); + expectedRegular.add(two); + assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test")); + assertTrue( + ArrayUtils.isEmpty(mQuotaController.getHpjTimingSessions(0, "com.android.test"))); + + mQuotaController.saveTimingSession(0, "com.android.test", thr, true); + expectedHpj.add(thr); + assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test")); + assertEquals(expectedHpj, mQuotaController.getHpjTimingSessions(0, "com.android.test")); + + mQuotaController.saveTimingSession(0, "com.android.test", fou, false); + mQuotaController.saveTimingSession(0, "com.android.test", fou, true); + expectedRegular.add(fou); + expectedHpj.add(fou); + assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test")); + assertEquals(expectedHpj, mQuotaController.getHpjTimingSessions(0, "com.android.test")); } @Test @@ -393,35 +428,48 @@ public class QuotaControllerTest { // Way past the 24 hour boundary. TimingSession fiv = createTimingSession( now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4); - List<TimingSession> expected = new ArrayList<>(); + List<TimingSession> expectedRegular = new ArrayList<>(); + List<TimingSession> expectedHpj = new ArrayList<>(); // Added in correct (chronological) order. - expected.add(fou); - expected.add(thr); - expected.add(two); - expected.add(one); - mQuotaController.saveTimingSession(0, "com.android.test", fiv); - mQuotaController.saveTimingSession(0, "com.android.test", fou); - mQuotaController.saveTimingSession(0, "com.android.test", thr); - mQuotaController.saveTimingSession(0, "com.android.test", two); - mQuotaController.saveTimingSession(0, "com.android.test", one); + expectedRegular.add(fou); + expectedRegular.add(thr); + expectedRegular.add(two); + expectedRegular.add(one); + expectedHpj.add(fiv); // HPJ list should be unaffected + expectedHpj.add(fou); + expectedHpj.add(one); + mQuotaController.saveTimingSession(0, "com.android.test", fiv, false); + mQuotaController.saveTimingSession(0, "com.android.test", fou, false); + mQuotaController.saveTimingSession(0, "com.android.test", thr, false); + mQuotaController.saveTimingSession(0, "com.android.test", two, false); + mQuotaController.saveTimingSession(0, "com.android.test", one, false); + mQuotaController.saveTimingSession(0, "com.android.test", fiv, true); + mQuotaController.saveTimingSession(0, "com.android.test", fou, true); + mQuotaController.saveTimingSession(0, "com.android.test", one, true); synchronized (mQuotaController.mLock) { mQuotaController.deleteObsoleteSessionsLocked(); } - assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test")); + assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test")); + assertEquals(expectedHpj, mQuotaController.getHpjTimingSessions(0, "com.android.test")); } @Test public void testOnAppRemovedLocked() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test.remove", - createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test.remove", createTimingSession( - now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5)); + now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5), + false); + mQuotaController.saveTimingSession(0, "com.android.test.remove", + createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false); mQuotaController.saveTimingSession(0, "com.android.test.remove", - createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1)); + createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true); + mQuotaController.saveTimingSession(0, "com.android.test.remove", + createTimingSession(now - (15 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true); // Test that another app isn't affected. TimingSession one = createTimingSession( now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3); @@ -431,8 +479,14 @@ public class QuotaControllerTest { // Added in correct (chronological) order. expected.add(two); expected.add(one); - mQuotaController.saveTimingSession(0, "com.android.test.stay", two); - mQuotaController.saveTimingSession(0, "com.android.test.stay", one); + mQuotaController.saveTimingSession(0, "com.android.test.stay", two, false); + mQuotaController.saveTimingSession(0, "com.android.test.stay", one, false); + mQuotaController.saveTimingSession(0, "com.android.test.stay", one, true); + + assertNotNull(mQuotaController.getTimingSessions(0, "com.android.test.remove")); + assertNotNull(mQuotaController.getHpjTimingSessions(0, "com.android.test.remove")); + assertNotNull(mQuotaController.getTimingSessions(0, "com.android.test.stay")); + assertNotNull(mQuotaController.getHpjTimingSessions(0, "com.android.test.stay")); ExecutionStats expectedStats = new ExecutionStats(); expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS; @@ -445,6 +499,7 @@ public class QuotaControllerTest { mQuotaController.onAppRemovedLocked("com.android.test.remove", uid); } assertNull(mQuotaController.getTimingSessions(0, "com.android.test.remove")); + assertNull(mQuotaController.getHpjTimingSessions(0, "com.android.test.remove")); assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test.stay")); synchronized (mQuotaController.mLock) { assertEquals(expectedStats, @@ -462,23 +517,36 @@ public class QuotaControllerTest { public void testOnUserRemovedLocked() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession( - now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5)); + now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5), + false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1)); + createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - (15 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true); // Test that another user isn't affected. TimingSession one = createTimingSession( now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3); TimingSession two = createTimingSession( now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); - List<TimingSession> expected = new ArrayList<>(); + List<TimingSession> expectedRegular = new ArrayList<>(); + List<TimingSession> expectedHpj = new ArrayList<>(); // Added in correct (chronological) order. - expected.add(two); - expected.add(one); - mQuotaController.saveTimingSession(10, "com.android.test", two); - mQuotaController.saveTimingSession(10, "com.android.test", one); + expectedRegular.add(two); + expectedRegular.add(one); + expectedHpj.add(one); + mQuotaController.saveTimingSession(10, "com.android.test", two, false); + mQuotaController.saveTimingSession(10, "com.android.test", one, false); + mQuotaController.saveTimingSession(10, "com.android.test", one, true); + + assertNotNull(mQuotaController.getTimingSessions(0, "com.android.test")); + assertNotNull(mQuotaController.getHpjTimingSessions(0, "com.android.test")); + assertNotNull(mQuotaController.getTimingSessions(10, "com.android.test")); + assertNotNull(mQuotaController.getHpjTimingSessions(10, "com.android.test")); ExecutionStats expectedStats = new ExecutionStats(); expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS; @@ -489,7 +557,11 @@ public class QuotaControllerTest { synchronized (mQuotaController.mLock) { mQuotaController.onUserRemovedLocked(0); assertNull(mQuotaController.getTimingSessions(0, "com.android.test")); - assertEquals(expected, mQuotaController.getTimingSessions(10, "com.android.test")); + assertNull(mQuotaController.getHpjTimingSessions(0, "com.android.test")); + assertEquals(expectedRegular, + mQuotaController.getTimingSessions(10, "com.android.test")); + assertEquals(expectedHpj, + mQuotaController.getHpjTimingSessions(10, "com.android.test")); assertEquals(expectedStats, mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); assertNotEquals(expectedStats, @@ -502,17 +574,19 @@ public class QuotaControllerTest { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); // Added in chronological order. mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession( - now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5)); + now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5), + false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1)); + createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession( - now - (HOUR_IN_MILLIS - 10 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1)); + now - (HOUR_IN_MILLIS - 10 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), + false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 5 * MINUTE_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3)); + createTimingSession(now - 5 * MINUTE_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3), false); // Test an app that hasn't had any activity. ExecutionStats expectedStats = new ExecutionStats(); @@ -703,7 +777,6 @@ public class QuotaControllerTest { @Test public void testUpdateExecutionStatsLocked_WithTimer() { - final long now = sElapsedRealtimeClock.millis(); setProcessState(ActivityManager.PROCESS_STATE_SERVICE); ExecutionStats expectedStats = new ExecutionStats(); @@ -741,7 +814,7 @@ public class QuotaControllerTest { // Add old session. Make sure values are combined correctly. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(sElapsedRealtimeClock.millis() - (6 * HOUR_IN_MILLIS), - 10 * MINUTE_IN_MILLIS, 5)); + 10 * MINUTE_IN_MILLIS, 5), false); expectedStats.sessionCountInWindow = 1; expectedStats.expirationTimeElapsed = sElapsedRealtimeClock.millis() + 18 * HOUR_IN_MILLIS; @@ -794,13 +867,13 @@ public class QuotaControllerTest { public void testGetExecutionStatsLocked_Values() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); ExecutionStats expectedStats = new ExecutionStats(); @@ -882,7 +955,7 @@ public class QuotaControllerTest { advanceElapsedClock(3 * MINUTE_IN_MILLIS); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2)); + createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false); ExecutionStats expectedStats = new ExecutionStats(); @@ -942,24 +1015,24 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession( JobSchedulerService.sElapsedRealtimeClock.millis(), - 5 * MINUTE_IN_MILLIS, 5)); + 5 * MINUTE_IN_MILLIS, 5), false); advanceElapsedClock(5 * MINUTE_IN_MILLIS); advanceElapsedClock(5 * MINUTE_IN_MILLIS); for (int j = 0; j < 5; ++j) { mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession( JobSchedulerService.sElapsedRealtimeClock.millis(), - MINUTE_IN_MILLIS, 2)); + MINUTE_IN_MILLIS, 2), false); advanceElapsedClock(MINUTE_IN_MILLIS); advanceElapsedClock(54 * SECOND_IN_MILLIS); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession( - JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1)); + JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false); advanceElapsedClock(500); advanceElapsedClock(400); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession( - JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1)); + JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false); advanceElapsedClock(100); advanceElapsedClock(5 * SECOND_IN_MILLIS); } @@ -1095,13 +1168,13 @@ public class QuotaControllerTest { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); final ExecutionStats originalStatsActive; final ExecutionStats originalStatsWorking; final ExecutionStats originalStatsFrequent; @@ -1195,20 +1268,20 @@ public class QuotaControllerTest { public void testGetMaxJobExecutionTimeLocked() { mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS, 5)); + 3 * MINUTE_IN_MILLIS, 5), false); JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0); job.setStandbyBucket(RARE_INDEX); setCharging(); synchronized (mQuotaController.mLock) { - assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, + assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); } setDischarging(); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); synchronized (mQuotaController.mLock) { - assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, + assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); } @@ -1220,7 +1293,7 @@ public class QuotaControllerTest { } setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); synchronized (mQuotaController.mLock) { - assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, + assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); mQuotaController.maybeStopTrackingJobLocked(job, null, false); } @@ -1242,17 +1315,17 @@ public class QuotaControllerTest { // Close to RARE boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS), - 30 * SECOND_IN_MILLIS, 5)); + 30 * SECOND_IN_MILLIS, 5), false); // Far away from FREQUENT boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); // Overlap WORKING_SET boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS, 5)); + 3 * MINUTE_IN_MILLIS, 5), false); // Close to ACTIVE boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); setStandbyBucket(RARE_INDEX); synchronized (mQuotaController.mLock) { @@ -1306,7 +1379,8 @@ public class QuotaControllerTest { // Overlap boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( - now - (24 * HOUR_IN_MILLIS + 8 * MINUTE_IN_MILLIS), 4 * HOUR_IN_MILLIS, 5)); + now - (24 * HOUR_IN_MILLIS + 8 * MINUTE_IN_MILLIS), 4 * HOUR_IN_MILLIS, 5), + false); setStandbyBucket(WORKING_INDEX); synchronized (mQuotaController.mLock) { @@ -1323,7 +1397,7 @@ public class QuotaControllerTest { // Close to boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (24 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS), - 4 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS, 5)); + 4 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS, 5), false); setStandbyBucket(WORKING_INDEX); synchronized (mQuotaController.mLock) { @@ -1339,7 +1413,8 @@ public class QuotaControllerTest { // Far from boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( - now - (20 * HOUR_IN_MILLIS), 4 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS, 5)); + now - (20 * HOUR_IN_MILLIS), 4 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS, 5), + false); setStandbyBucket(WORKING_INDEX); synchronized (mQuotaController.mLock) { @@ -1366,10 +1441,11 @@ public class QuotaControllerTest { createTimingSession( now - (24 * HOUR_IN_MILLIS + 11 * MINUTE_IN_MILLIS), 4 * HOUR_IN_MILLIS, - 5)); + 5), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( - now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); + now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), + false); synchronized (mQuotaController.mLock) { // Both max and bucket time have 8 minutes left. @@ -1387,15 +1463,17 @@ public class QuotaControllerTest { // Overlap boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( - now - (24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 5)); + now - (24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 5), + false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( now - (20 * HOUR_IN_MILLIS), 3 * HOUR_IN_MILLIS + 48 * MINUTE_IN_MILLIS, - 5)); + 5), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( - now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); + now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), + false); synchronized (mQuotaController.mLock) { // Both max and bucket time have 8 minutes left. @@ -1430,9 +1508,9 @@ public class QuotaControllerTest { setDischarging(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); synchronized (mQuotaController.mLock) { mQuotaController.incrementJobCountLocked(0, "com.android.test", 5); assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); @@ -1445,9 +1523,10 @@ public class QuotaControllerTest { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; mQuotaController.saveTimingSession(0, "com.android.test.spam", - createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25)); + createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25), false); mQuotaController.saveTimingSession(0, "com.android.test.spam", - createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount)); + createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount), + false); synchronized (mQuotaController.mLock) { mQuotaController.incrementJobCountLocked(0, "com.android.test.spam", jobCount); assertFalse(mQuotaController.isWithinQuotaLocked( @@ -1455,9 +1534,10 @@ public class QuotaControllerTest { } mQuotaController.saveTimingSession(0, "com.android.test.frequent", - createTimingSession(now - (2 * HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 2000)); + createTimingSession(now - (2 * HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 2000), + false); mQuotaController.saveTimingSession(0, "com.android.test.frequent", - createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500)); + createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500), false); synchronized (mQuotaController.mLock) { assertFalse(mQuotaController.isWithinQuotaLocked( 0, "com.android.test.frequent", FREQUENT_INDEX)); @@ -1469,11 +1549,11 @@ public class QuotaControllerTest { setDischarging(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5)); + createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), false); synchronized (mQuotaController.mLock) { mQuotaController.incrementJobCountLocked(0, "com.android.test", 5); assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); @@ -1486,9 +1566,10 @@ public class QuotaControllerTest { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25)); + createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount)); + createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount), + false); synchronized (mQuotaController.mLock) { mQuotaController.incrementJobCountLocked(0, "com.android.test", jobCount); assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); @@ -1639,7 +1720,7 @@ public class QuotaControllerTest { for (int i = 0; i < 7; ++i) { mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - ((10 - i) * MINUTE_IN_MILLIS), 30 * SECOND_IN_MILLIS, - 2)); + 2), false); synchronized (mQuotaController.mLock) { mQuotaController.incrementJobCountLocked(0, "com.android.test", 2); @@ -1673,7 +1754,7 @@ public class QuotaControllerTest { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final long end = now - (6 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); mQuotaController.saveTimingSession(0, "com.android.test", - new TimingSession(now - 6 * HOUR_IN_MILLIS, end, 1)); + new TimingSession(now - 6 * HOUR_IN_MILLIS, end, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleCleanupAlarmLocked(); } @@ -1682,9 +1763,9 @@ public class QuotaControllerTest { // Test with new (more recent) timing sessions saved. AlarmManger shouldn't be called again. mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); + createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1)); + createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleCleanupAlarmLocked(); } @@ -1715,11 +1796,11 @@ public class QuotaControllerTest { final long expectedAlarmTime = (now - 18 * HOUR_IN_MILLIS) + 24 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - 18 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1)); + createTimingSession(now - 18 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - 12 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1)); + createTimingSession(now - 12 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - 7 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1)); + createTimingSession(now - 7 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked( SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); @@ -1727,7 +1808,7 @@ public class QuotaControllerTest { verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - 2 * HOUR_IN_MILLIS, 55 * MINUTE_IN_MILLIS, 1)); + createTimingSession(now - 2 * HOUR_IN_MILLIS, 55 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked( SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); @@ -1772,7 +1853,7 @@ public class QuotaControllerTest { // Test with timing sessions out of window. final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); + createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1784,7 +1865,7 @@ public class QuotaControllerTest { final long expectedAlarmTime = end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(0, "com.android.test", - new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1)); + new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1792,9 +1873,9 @@ public class QuotaControllerTest { // Add some more sessions, but still in quota. mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); + createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1)); + createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1802,7 +1883,7 @@ public class QuotaControllerTest { // Test when out of quota. mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); + createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1836,7 +1917,7 @@ public class QuotaControllerTest { // Test with timing sessions out of window. final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); + createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1846,7 +1927,7 @@ public class QuotaControllerTest { final long start = now - (6 * HOUR_IN_MILLIS); final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1)); + createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1854,9 +1935,9 @@ public class QuotaControllerTest { // Add some more sessions, but still in quota. mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); + createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1)); + createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1864,7 +1945,7 @@ public class QuotaControllerTest { // Test when out of quota. mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); + createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1901,7 +1982,7 @@ public class QuotaControllerTest { // Test with timing sessions out of window. final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); + createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1915,7 +1996,7 @@ public class QuotaControllerTest { start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1)); + createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1923,9 +2004,9 @@ public class QuotaControllerTest { // Add some more sessions, but still in quota. mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); + createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1)); + createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1933,7 +2014,7 @@ public class QuotaControllerTest { // Test when out of quota. mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1)); + createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); } @@ -1960,17 +2041,17 @@ public class QuotaControllerTest { // Affects rare bucket mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3)); + createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3), false); // Affects frequent and rare buckets mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 4 * HOUR_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3)); + createTimingSession(now - 4 * HOUR_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3), false); // Affects working, frequent, and rare buckets final long outOfQuotaTime = now - HOUR_IN_MILLIS; mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(outOfQuotaTime, 7 * MINUTE_IN_MILLIS, 10)); + createTimingSession(outOfQuotaTime, 7 * MINUTE_IN_MILLIS, 10), false); // Affects all buckets mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - 5 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 3)); + createTimingSession(now - 5 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 3), false); InOrder inOrder = inOrder(mAlarmManager); @@ -2143,9 +2224,9 @@ public class QuotaControllerTest { // the quota. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (2 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS + contributionMs, 3)); + 3 * MINUTE_IN_MILLIS + contributionMs, 3), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - HOUR_IN_MILLIS, remainingTimeMs, 2)); + createTimingSession(now - HOUR_IN_MILLIS, remainingTimeMs, 2), false); // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which // is 2 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session. final long expectedAlarmTime = now - HOUR_IN_MILLIS + 2 * HOUR_IN_MILLIS @@ -2175,9 +2256,9 @@ public class QuotaControllerTest { // the quota. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (24 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS + contributionMs, 3)); + 3 * MINUTE_IN_MILLIS + contributionMs, 3), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - 20 * HOUR_IN_MILLIS, remainingTimeMs, 300)); + createTimingSession(now - 20 * HOUR_IN_MILLIS, remainingTimeMs, 300), false); // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which // is 24 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session. final long expectedAlarmTime = now - 20 * HOUR_IN_MILLIS @@ -2217,6 +2298,16 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 10 * SECOND_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 7 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 2 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 90 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 1 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 30 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, 27 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_WINDOW_SIZE_MS, 12 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS, 10 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_TOP_APP_MS, 87 * SECOND_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_INTERACTION_MS, 86 * SECOND_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS, 85 * SECOND_IN_MILLIS); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); @@ -2244,6 +2335,16 @@ public class QuotaControllerTest { assertEquals(10 * SECOND_IN_MILLIS, mQuotaController.getTimingSessionCoalescingDurationMs()); assertEquals(7 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs()); + assertEquals(2 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[ACTIVE_INDEX]); + assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[WORKING_INDEX]); + assertEquals(HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[FREQUENT_INDEX]); + assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[RARE_INDEX]); + assertEquals(27 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[RESTRICTED_INDEX]); + assertEquals(12 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitWindowSizeMs()); + assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getHpjTopAppTimeChunkSizeMs()); + assertEquals(87 * SECOND_IN_MILLIS, mQuotaController.getHpjRewardTopAppMs()); + assertEquals(86 * SECOND_IN_MILLIS, mQuotaController.getHpjRewardInteractionMs()); + assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getHpjRewardNotificationSeenMs()); } @Test @@ -2272,6 +2373,16 @@ public class QuotaControllerTest { setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 0); setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, -1); setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, -1); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, -1); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, -1); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, -1); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, -1); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, -1); + setDeviceConfigLong(QcConstants.KEY_HPJ_WINDOW_SIZE_MS, -1); + setDeviceConfigLong(QcConstants.KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS, -1); + setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_TOP_APP_MS, -1); + setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_INTERACTION_MS, -1); + setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS, -1); assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(0, mQuotaController.getInQuotaBufferMs()); @@ -2296,6 +2407,16 @@ public class QuotaControllerTest { assertEquals(0, mQuotaController.getBucketMaxSessionCounts()[RESTRICTED_INDEX]); assertEquals(0, mQuotaController.getTimingSessionCoalescingDurationMs()); assertEquals(0, mQuotaController.getMinQuotaCheckDelayMs()); + assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[ACTIVE_INDEX]); + assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[WORKING_INDEX]); + assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[FREQUENT_INDEX]); + assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[RARE_INDEX]); + assertEquals(0, mQuotaController.getHpjLimitsMs()[RESTRICTED_INDEX]); + assertEquals(HOUR_IN_MILLIS, mQuotaController.getHpjLimitWindowSizeMs()); + assertEquals(1, mQuotaController.getHpjTopAppTimeChunkSizeMs()); + assertEquals(10 * SECOND_IN_MILLIS, mQuotaController.getHpjRewardTopAppMs()); + assertEquals(5 * SECOND_IN_MILLIS, mQuotaController.getHpjRewardInteractionMs()); + assertEquals(0, mQuotaController.getHpjRewardNotificationSeenMs()); // Invalid configurations. // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD @@ -2318,6 +2439,16 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_INTERACTION_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); @@ -2332,6 +2463,16 @@ public class QuotaControllerTest { assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getTimingSessionCoalescingDurationMs()); assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs()); + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[ACTIVE_INDEX]); + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[WORKING_INDEX]); + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[FREQUENT_INDEX]); + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[RARE_INDEX]); + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[RESTRICTED_INDEX]); + assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitWindowSizeMs()); + assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getHpjTopAppTimeChunkSizeMs()); + assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getHpjRewardTopAppMs()); + assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getHpjRewardInteractionMs()); + assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getHpjRewardNotificationSeenMs()); } /** Tests that TimingSessions aren't saved when the device is charging. */ @@ -2937,7 +3078,7 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, - 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1)); + 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false); InOrder inOrder = inOrder(mJobSchedulerService); @@ -3031,7 +3172,7 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, - 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1)); + 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false); // Start the job. synchronized (mQuotaController.mLock) { @@ -3069,10 +3210,10 @@ public class QuotaControllerTest { // window, so as the package "reaches its quota" it will have more to keep running. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 2 * HOUR_IN_MILLIS, - 10 * SECOND_IN_MILLIS - remainingTimeMs, 1)); + 10 * SECOND_IN_MILLIS - remainingTimeMs, 1), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - HOUR_IN_MILLIS, - 9 * MINUTE_IN_MILLIS + 50 * SECOND_IN_MILLIS, 1)); + 9 * MINUTE_IN_MILLIS + 50 * SECOND_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { assertEquals(remainingTimeMs, @@ -3098,7 +3239,9 @@ public class QuotaControllerTest { } // Handler is told to check when the quota will be consumed, not when the initial // remaining time is over. - verify(handler, atLeast(1)).sendMessageDelayed(any(), eq(10 * SECOND_IN_MILLIS)); + verify(handler, atLeast(1)).sendMessageDelayed( + argThat(msg -> msg.what == QuotaController.MSG_REACHED_QUOTA), + eq(10 * SECOND_IN_MILLIS)); verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs)); // After 10 seconds, the job should finally be out of quota. @@ -3242,4 +3385,1337 @@ public class QuotaControllerTest { verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); } + + @Test + public void testGetRemainingHpjExecutionTimeLocked_NoHistory() { + final long[] limits = mQuotaController.getHpjLimitsMs(); + for (int i = 0; i < limits.length; ++i) { + setStandbyBucket(i); + assertEquals("Got wrong remaining HPJ execution time for bucket #" + i, + limits[i], + mQuotaController.getRemainingHpjExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + @Test + public void testGetRemainingHpjExecutionTimeLocked_AllSessionsWithinWindow() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - mQcConstants.HPJ_WINDOW_SIZE_MS, MINUTE_IN_MILLIS, 5), + true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + + final long[] limits = mQuotaController.getHpjLimitsMs(); + for (int i = 0; i < limits.length; ++i) { + setStandbyBucket(i); + assertEquals("Got wrong remaining HPJ execution time for bucket #" + i, + i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS), + mQuotaController.getRemainingHpjExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + @Test + public void testGetRemainingHpjExecutionTimeLocked_OneSessionStraddlesEdge() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + final long[] limits = mQuotaController.getHpjLimitsMs(); + for (int i = 0; i < limits.length; ++i) { + synchronized (mQuotaController.mLock) { + mQuotaController.onUserRemovedLocked(SOURCE_USER_ID); + } + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS), + 2 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + + setStandbyBucket(i); + assertEquals("Got wrong remaining HPJ execution time for bucket #" + i, + i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS), + mQuotaController.getRemainingHpjExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + @Test + public void testGetRemainingHpjExecutionTimeLocked_WithStaleSessions() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + + final long[] limits = mQuotaController.getHpjLimitsMs(); + for (int i = 0; i < limits.length; ++i) { + synchronized (mQuotaController.mLock) { + mQuotaController.onUserRemovedLocked(SOURCE_USER_ID); + } + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession( + now - (mQcConstants.HPJ_WINDOW_SIZE_MS + 10 * MINUTE_IN_MILLIS), + 2 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession( + now - (mQcConstants.HPJ_WINDOW_SIZE_MS + 5 * MINUTE_IN_MILLIS), + MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS), + 2 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + + setStandbyBucket(i); + assertEquals("Got wrong remaining HPJ execution time for bucket #" + i, + i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS), + mQuotaController.getRemainingHpjExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + /** + * Tests that getRemainingHpjExecutionTimeLocked returns the correct stats soon after device + * startup. + */ + @Test + public void testGetRemainingHpjExecutionTimeLocked_BeginningOfTime() { + // Set time to 3 minutes after boot. + advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis()); + advanceElapsedClock(3 * MINUTE_IN_MILLIS); + + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(150 * SECOND_IN_MILLIS, 15 * SECOND_IN_MILLIS, 5), true); + + final long[] limits = mQuotaController.getHpjLimitsMs(); + for (int i = 0; i < limits.length; ++i) { + setStandbyBucket(i); + assertEquals("Got wrong remaining HPJ execution time for bucket #" + i, + i == NEVER_INDEX ? 0 : (limits[i] - 75 * SECOND_IN_MILLIS), + mQuotaController.getRemainingHpjExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + @Test + public void testGetTimeUntilHpjQuotaConsumedLocked_NoHistory() { + final long[] limits = mQuotaController.getHpjLimitsMs(); + for (int i = 0; i < limits.length; ++i) { + setStandbyBucket(i); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + i, + limits[i], mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + @Test + public void testGetTimeUntilHpjQuotaConsumedLocked_AllSessionsWithinWindow() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 20 * MINUTE_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + + final long[] limits = mQuotaController.getHpjLimitsMs(); + for (int i = 0; i < limits.length; ++i) { + setStandbyBucket(i); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + i, + i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS), + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + @Test + public void testGetTimeUntilHpjQuotaConsumedLocked_SessionsAtEdgeOfWindow() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - mQcConstants.HPJ_WINDOW_SIZE_MS, MINUTE_IN_MILLIS, 5), + true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS - 2 * MINUTE_IN_MILLIS), + MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS - 10 * MINUTE_IN_MILLIS), + MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS); + + setStandbyBucket(ACTIVE_INDEX); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + ACTIVE_INDEX, + 28 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(WORKING_INDEX); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + WORKING_INDEX, + 18 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(FREQUENT_INDEX); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + FREQUENT_INDEX, + 13 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(RARE_INDEX); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + RARE_INDEX, + 7 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(RESTRICTED_INDEX); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + RESTRICTED_INDEX, + MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + @Test + public void testGetTimeUntilHpjQuotaConsumedLocked_OneSessionStraddlesEdge() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS), + 2 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS); + + setStandbyBucket(ACTIVE_INDEX); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + ACTIVE_INDEX, + 26 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(WORKING_INDEX); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + WORKING_INDEX, + 16 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(FREQUENT_INDEX); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + FREQUENT_INDEX, + 11 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(RARE_INDEX); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + RARE_INDEX, + 6 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(RESTRICTED_INDEX); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + RESTRICTED_INDEX, + MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + @Test + public void testGetTimeUntilHpjQuotaConsumedLocked_WithStaleSessions() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + + List<TimingSession> timingSessions = new ArrayList<>(); + timingSessions.add( + createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + 10 * MINUTE_IN_MILLIS), + 2 * MINUTE_IN_MILLIS, 5)); + timingSessions.add( + createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + 5 * MINUTE_IN_MILLIS), + MINUTE_IN_MILLIS, 5)); + timingSessions.add( + createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS), + 2 * MINUTE_IN_MILLIS, 5)); + timingSessions.add( + createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5)); + timingSessions.add( + createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5)); + timingSessions.add( + createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5)); + timingSessions.add( + createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5)); + + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS); + + runTestGetTimeUntilHpjQuotaConsumedLocked( + timingSessions, ACTIVE_INDEX, 26 * MINUTE_IN_MILLIS); + runTestGetTimeUntilHpjQuotaConsumedLocked( + timingSessions, WORKING_INDEX, 16 * MINUTE_IN_MILLIS); + runTestGetTimeUntilHpjQuotaConsumedLocked( + timingSessions, FREQUENT_INDEX, 11 * MINUTE_IN_MILLIS); + runTestGetTimeUntilHpjQuotaConsumedLocked(timingSessions, RARE_INDEX, 6 * MINUTE_IN_MILLIS); + runTestGetTimeUntilHpjQuotaConsumedLocked( + timingSessions, RESTRICTED_INDEX, MINUTE_IN_MILLIS); + } + + /** + * Tests that getTimeUntilHpjQuotaConsumedLocked returns the correct stats soon after device + * startup. + */ + @Test + public void testGetTimeUntilHpjQuotaConsumedLocked_BeginningOfTime() { + // Set time to 3 minutes after boot. + advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis()); + advanceElapsedClock(3 * MINUTE_IN_MILLIS); + + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(150 * SECOND_IN_MILLIS, 15 * SECOND_IN_MILLIS, 5), true); + + final long[] limits = mQuotaController.getHpjLimitsMs(); + for (int i = 0; i < limits.length; ++i) { + setStandbyBucket(i); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + i, + limits[i], // All existing sessions will phase out + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + private void runTestGetTimeUntilHpjQuotaConsumedLocked( + List<TimingSession> timingSessions, int bucketIndex, long expectedValue) { + synchronized (mQuotaController.mLock) { + mQuotaController.onUserRemovedLocked(SOURCE_USER_ID); + } + if (timingSessions != null) { + for (TimingSession session : timingSessions) { + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, session, true); + } + } + + setStandbyBucket(bucketIndex); + assertEquals("Got wrong time until HPJ quota consumed for bucket #" + bucketIndex, + expectedValue, + mQuotaController.getTimeUntilHpjQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + @Test + public void testMaybeScheduleStartAlarmLocked_Hpj() { + // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests + // because it schedules an alarm too. Prevent it from doing so. + spyOn(mQuotaController); + doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); + + final int standbyBucket = WORKING_INDEX; + setStandbyBucket(standbyBucket); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS); + + InOrder inOrder = inOrder(mAlarmManager); + + synchronized (mQuotaController.mLock) { + // No sessions saved yet. + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test with timing sessions out of window. + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 25 * HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS, 1), true); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test with timing sessions in window but still in quota. + final long end = now - (22 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); + final long expectedAlarmTime = now + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + new TimingSession(now - 22 * HOUR_IN_MILLIS, end, 1), true); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Add some more sessions, but still in quota. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (50 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 1), true); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + + // Test when out of quota. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 30 * MINUTE_IN_MILLIS, 6 * MINUTE_IN_MILLIS, 1), true); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + // Alarm already scheduled, so make sure it's not scheduled again. + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + } + + /** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */ + @Test + public void testMaybeScheduleStartAlarmLocked_Hpj_BucketChange() { + // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests + // because it schedules an alarm too. Prevent it from doing so. + spyOn(mQuotaController); + doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); + + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); + + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + // Affects active bucket + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3), true); + // Affects active and working buckets + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 4 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 3), true); + // Affects active, working, and frequent buckets + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 10), true); + // Affects all buckets + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 5 * MINUTE_IN_MILLIS, 10 * MINUTE_IN_MILLIS, 3), true); + + InOrder inOrder = inOrder(mAlarmManager); + + // Start in ACTIVE bucket. + setStandbyBucket(ACTIVE_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX); + } + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class)); + + // And down from there. + setStandbyBucket(WORKING_INDEX); + final long expectedWorkingAlarmTime = + (now - 4 * HOUR_IN_MILLIS) + (24 * HOUR_IN_MILLIS) + + mQcConstants.IN_QUOTA_BUFFER_MS; + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX); + } + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + setStandbyBucket(FREQUENT_INDEX); + final long expectedFrequentAlarmTime = + (now - HOUR_IN_MILLIS) + (24 * HOUR_IN_MILLIS) + mQcConstants.IN_QUOTA_BUFFER_MS; + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX); + } + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + setStandbyBucket(RARE_INDEX); + final long expectedRareAlarmTime = + (now - 5 * MINUTE_IN_MILLIS) + (24 * HOUR_IN_MILLIS) + + mQcConstants.IN_QUOTA_BUFFER_MS; + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX); + } + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + // And back up again. + setStandbyBucket(FREQUENT_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX); + } + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + setStandbyBucket(WORKING_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX); + } + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + + setStandbyBucket(ACTIVE_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX); + } + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); + inOrder.verify(mAlarmManager, times(1)).cancel(any(AlarmManager.OnAlarmListener.class)); + } + + /** + * Tests that the start alarm is properly rescheduled if the earliest session that contributes + * to the app being out of quota contributes less than the quota buffer time. + */ + @Test + public void testMaybeScheduleStartAlarmLocked_Hpj_SmallRollingQuota() { + // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests + // because it schedules an alarm too. Prevent it from doing so. + spyOn(mQuotaController); + doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); + + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + setStandbyBucket(WORKING_INDEX); + final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2; + final long remainingTimeMs = mQcConstants.HPJ_LIMIT_WORKING_MS - contributionMs; + + // Session straddles edge of bucket window. Only the contribution should be counted towards + // the quota. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (24 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS), + 3 * MINUTE_IN_MILLIS + contributionMs, 3), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 23 * HOUR_IN_MILLIS, remainingTimeMs, 2), true); + // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which + // is 24 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session. + final long expectedAlarmTime = + now + HOUR_IN_MILLIS + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX); + } + verify(mAlarmManager, times(1)) + .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); + } + + /** Tests that TimingSessions aren't saved when the device is charging. */ + @Test + public void testHpjTimerTracking_Charging() { + setCharging(); + + JobStatus jobStatus = createHpjJobStatus("testHpjTimerTracking_Charging", 1); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } + + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** Tests that TimingSessions are saved properly when the device is discharging. */ + @Test + public void testHpjTimerTracking_Discharging() { + setDischarging(); + setProcessState(ActivityManager.PROCESS_STATE_BACKUP); + + JobStatus jobStatus = createHpjJobStatus("testHpjTimerTracking_Discharging", 1); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } + + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + List<TimingSession> expected = new ArrayList<>(); + + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } + expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Test overlapping jobs. + JobStatus jobStatus2 = createHpjJobStatus("testHpjTimerTracking_Discharging", 2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + } + + JobStatus jobStatus3 = createHpjJobStatus("testHpjTimerTracking_Discharging", 3); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + } + + advanceElapsedClock(SECOND_IN_MILLIS); + + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus3); + } + advanceElapsedClock(20 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + } + expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3)); + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** + * Tests that TimingSessions are saved properly when the device alternates between + * charging and discharging. + */ + @Test + public void testHpjTimerTracking_ChargingAndDischarging() { + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + + JobStatus jobStatus = createHpjJobStatus("testHpjTimerTracking_ChargingAndDischarging", 1); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } + JobStatus jobStatus2 = createHpjJobStatus("testHpjTimerTracking_ChargingAndDischarging", 2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + } + JobStatus jobStatus3 = createHpjJobStatus("testHpjTimerTracking_ChargingAndDischarging", 3); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + } + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + List<TimingSession> expected = new ArrayList<>(); + + // A job starting while charging. Only the portion that runs during the discharging period + // should be counted. + setCharging(); + + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + setDischarging(); + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // One job starts while discharging, spans a charging session, and ends after the charging + // session. Only the portions during the discharging periods should be counted. This should + // result in two TimingSessions. A second job starts while discharging and ends within the + // charging session. Only the portion during the first discharging portion should be + // counted. A third job starts and ends within the charging session. The third job + // shouldn't be included in either job count. + setDischarging(); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + setCharging(); + expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus3); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + setDischarging(); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + advanceElapsedClock(20 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } + expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // A job starting while discharging and ending while charging. Only the portion that runs + // during the discharging period should be counted. + setDischarging(); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + mQuotaController.prepareForExecutionLocked(jobStatus2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setCharging(); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + } + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** Tests that TimingSessions are saved properly when all the jobs are background jobs. */ + @Test + public void testHpjTimerTracking_AllBackground() { + setDischarging(); + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + + JobStatus jobStatus = createHpjJobStatus("testHpjTimerTracking_AllBackground", 1); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + List<TimingSession> expected = new ArrayList<>(); + + // Test single job. + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } + expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Test overlapping jobs. + JobStatus jobStatus2 = createHpjJobStatus("testHpjTimerTracking_AllBackground", 2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + } + + JobStatus jobStatus3 = createHpjJobStatus("testHpjTimerTracking_AllBackground", 3); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + } + + advanceElapsedClock(SECOND_IN_MILLIS); + + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus3); + } + advanceElapsedClock(20 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + } + expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3)); + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** Tests that Timers don't count foreground jobs. */ + @Test + public void testHpjTimerTracking_AllForeground() { + setDischarging(); + + JobStatus jobStatus = createHpjJobStatus("testHpjTimerTracking_AllForeground", 1); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } + + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + // Change to a state that should still be considered foreground. + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + advanceElapsedClock(5 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** + * Tests that Timers properly track sessions when switching between foreground and background + * states. + */ + @Test + public void testHpjTimerTracking_ForegroundAndBackground() { + setDischarging(); + + JobStatus jobBg1 = createHpjJobStatus("testHpjTimerTracking_ForegroundAndBackground", 1); + JobStatus jobBg2 = createHpjJobStatus("testHpjTimerTracking_ForegroundAndBackground", 2); + JobStatus jobFg3 = createHpjJobStatus("testHpjTimerTracking_ForegroundAndBackground", 3); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); + } + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + List<TimingSession> expected = new ArrayList<>(); + + // UID starts out inactive. + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Bg job starts while inactive, spans an entire active session, and ends after the + // active session. + // App switching to foreground state then fg job starts. + // App remains in foreground state after coming to foreground, so there should only be one + // session. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.prepareForExecutionLocked(jobBg2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobFg3); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + } + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Bg job 1 starts, then fg job starts. Bg job 1 job ends. Shortly after, uid goes + // "inactive" and then bg job 2 starts. Then fg job ends. + // This should result in two TimingSessions: + // * The first should have a count of 1 + // * The second should have a count of 2 since it will include both jobs + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); + } + setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobFg3); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + } + expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** + * Tests that Timers properly track overlapping top and background jobs. + */ + @Test + public void testHpjTimerTracking_TopAndNonTop() { + setDischarging(); + + JobStatus jobBg1 = createHpjJobStatus("testHpjTimerTracking_TopAndNonTop", 1); + JobStatus jobBg2 = createHpjJobStatus("testHpjTimerTracking_TopAndNonTop", 2); + JobStatus jobFg1 = createHpjJobStatus("testHpjTimerTracking_TopAndNonTop", 3); + JobStatus jobTop = createHpjJobStatus("testHpjTimerTracking_TopAndNonTop", 4); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobTop, null); + } + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + List<TimingSession> expected = new ArrayList<>(); + + // UID starts out inactive. + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Bg job starts while inactive, spans an entire active session, and ends after the + // active session. + // App switching to top state then fg job starts. + // App remains in top state after coming to top, so there should only be one + // session. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.prepareForExecutionLocked(jobBg2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + } + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to + // foreground_service and a new job starts. Shortly after, uid goes + // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs. + // This should result in two TimingSessions: + // * The first should have a count of 1 + // * The second should have a count of 2, which accounts for the bg2 and fg, but not top + // jobs. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobTop, null); + } + setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobFg1); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false); + } + expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); + assertEquals(expected, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** + * Tests that HPJs aren't stopped when an app runs out of quota. + */ + @Test + public void testHpjTracking_OutOfQuota_ForegroundAndBackground() { + setDischarging(); + + JobStatus jobBg = + createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 1); + JobStatus jobTop = + createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 2); + JobStatus jobUnstarted = + createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 3); + trackJobs(jobBg, jobTop, jobUnstarted); + setStandbyBucket(WORKING_INDEX, jobTop, jobBg, jobUnstarted); + // Now the package only has 20 seconds to run. + final long remainingTimeMs = 20 * SECOND_IN_MILLIS; + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession( + JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, + mQcConstants.HPJ_LIMIT_WORKING_MS - remainingTimeMs, 1), true); + + InOrder inOrder = inOrder(mJobSchedulerService); + + // UID starts out inactive. + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + // Start the job. + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg); + } + advanceElapsedClock(remainingTimeMs / 2); + // New job starts after UID is in the foreground. Since the app is now in the foreground, it + // should continue to have remainingTimeMs / 2 time remaining. + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } + advanceElapsedClock(remainingTimeMs); + + // Wait for some extra time to allow for job processing. + inOrder.verify(mJobSchedulerService, + timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) + .onControllerStateChanged(); + synchronized (mQuotaController.mLock) { + assertEquals(remainingTimeMs / 2, + mQuotaController.getRemainingHpjExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + // Go to a background state. + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); + advanceElapsedClock(remainingTimeMs / 2 + 1); + inOrder.verify(mJobSchedulerService, + timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(); + // Top and bg HPJs should still be allowed to run since they started before the app ran + // out of quota. + assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertFalse(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + synchronized (mQuotaController.mLock) { + assertTrue( + 0 >= mQuotaController + .getRemainingHpjExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + // New jobs to run. + JobStatus jobBg2 = + createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 4); + JobStatus jobTop2 = + createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 5); + JobStatus jobFg = + createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 6); + setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg); + + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + // Confirm QC recognizes that jobUnstarted has changed from out-of-quota to in-quota. + inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(); + trackJobs(jobTop2, jobFg); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop2); + } + assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertTrue(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + + // App still in foreground so everything should be in quota. + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertTrue(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(); + // App is now in background and out of quota. Fg should now change to out of quota since it + // wasn't started. Top should remain in quota since it started when the app was in TOP. + assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + trackJobs(jobBg2); + assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + assertFalse(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + synchronized (mQuotaController.mLock) { + assertTrue( + 0 >= mQuotaController + .getRemainingHpjExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + /** + * Tests that Timers properly track overlapping top and background jobs. + */ + @Test + public void testHpjTimerTrackingSeparateFromRegularTracking() { + setDischarging(); + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + + JobStatus jobReg1 = createJobStatus("testHpjTimerTrackingSeparateFromRegularTracking", 1); + JobStatus jobHpj1 = + createHpjJobStatus("testHpjTimerTrackingSeparateFromRegularTracking", 2); + JobStatus jobReg2 = createJobStatus("testHpjTimerTrackingSeparateFromRegularTracking", 3); + JobStatus jobHpj2 = + createHpjJobStatus("testHpjTimerTrackingSeparateFromRegularTracking", 4); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobReg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobHpj1, null); + mQuotaController.maybeStartTrackingJobLocked(jobReg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobHpj2, null); + } + assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + List<TimingSession> expectedRegular = new ArrayList<>(); + List<TimingSession> expectedHpj = new ArrayList<>(); + + // First, regular job runs by itself. + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobReg1); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobReg1, jobReg1, true); + } + expectedRegular.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expectedRegular, + mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Next, HPJ runs by itself. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobHpj1); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobHpj1, null, false); + } + expectedHpj.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expectedRegular, + mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(expectedHpj, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + + // Finally, a regular job and HPJ happen to overlap runs. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobHpj2); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobReg2); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobHpj2, null, false); + } + expectedHpj.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + advanceElapsedClock(5 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobReg2, null, false); + } + expectedRegular.add( + createTimingSession(start + 5 * SECOND_IN_MILLIS, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expectedRegular, + mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(expectedHpj, + mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** + * Tests that a job is properly handled when it's at the edge of its quota and the old quota is + * being phased out. + */ + @Test + public void testHpjTracking_RollingQuota() { + JobStatus jobStatus = createHpjJobStatus("testHpjTracking_RollingQuota", 1); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } + setStandbyBucket(WORKING_INDEX, jobStatus); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + Handler handler = mQuotaController.getHandler(); + spyOn(handler); + + long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + final long remainingTimeMs = SECOND_IN_MILLIS; + // The package only has one second to run, but this session is at the edge of the rolling + // window, so as the package "reaches its quota" it will have more to keep running. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - mQcConstants.HPJ_WINDOW_SIZE_MS, + 10 * SECOND_IN_MILLIS - remainingTimeMs, 1), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - HOUR_IN_MILLIS, + mQcConstants.HPJ_LIMIT_WORKING_MS - 10 * SECOND_IN_MILLIS, 1), true); + + synchronized (mQuotaController.mLock) { + assertEquals(remainingTimeMs, + mQuotaController.getRemainingHpjExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Start the job. + mQuotaController.prepareForExecutionLocked(jobStatus); + } + advanceElapsedClock(remainingTimeMs); + + // Wait for some extra time to allow for job processing. + verify(mJobSchedulerService, + timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) + .onControllerStateChanged(); + assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA)); + // The job used up the remaining quota, but in that time, the same amount of time in the + // old TimingSession also fell out of the quota window, so it should still have the same + // amount of remaining time left its quota. + synchronized (mQuotaController.mLock) { + assertEquals(remainingTimeMs, + mQuotaController.getRemainingHpjExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + // Handler is told to check when the quota will be consumed, not when the initial + // remaining time is over. + verify(handler, atLeast(1)).sendMessageDelayed( + argThat(msg -> msg.what == QuotaController.MSG_REACHED_HPJ_QUOTA), + eq(10 * SECOND_IN_MILLIS)); + verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs)); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt new file mode 100644 index 000000000000..edae08a3bfe1 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -0,0 +1,662 @@ +/* + * 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.pm + +import android.content.Context +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.ApplicationInfo +import android.content.pm.FallbackCategoryProvider +import android.content.pm.FeatureInfo +import android.content.pm.PackageParser.SigningDetails +import android.content.pm.ResolveInfo +import android.content.pm.ServiceInfo +import android.content.pm.Signature +import android.content.pm.UserInfo +import android.content.pm.parsing.ParsingPackage +import android.content.pm.parsing.ParsingPackageUtils +import android.content.res.Resources +import android.hardware.display.DisplayManager +import android.os.Build +import android.os.Environment +import android.os.SystemProperties +import android.os.UserHandle +import android.os.UserManager +import android.os.incremental.IncrementalManager +import android.permission.IPermissionManager +import android.util.ArrayMap +import android.util.DisplayMetrics +import android.util.EventLog +import android.view.Display +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.dx.mockito.inline.extended.ExtendedMockito.any +import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean +import com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt +import com.android.dx.mockito.inline.extended.ExtendedMockito.anyString +import com.android.dx.mockito.inline.extended.ExtendedMockito.argThat +import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn +import com.android.dx.mockito.inline.extended.ExtendedMockito.eq +import com.android.dx.mockito.inline.extended.ExtendedMockito.spy +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder +import com.android.internal.R +import com.android.server.LocalServices +import com.android.server.LockGuard +import com.android.server.SystemConfig +import com.android.server.SystemServerInitThreadPool +import com.android.server.compat.PlatformCompat +import com.android.server.extendedtestutils.wheneverStatic +import com.android.server.pm.dex.DexManager +import com.android.server.pm.parsing.PackageParser2 +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.pm.parsing.pkg.PackageImpl +import com.android.server.pm.parsing.pkg.ParsedPackage +import com.android.server.pm.permission.PermissionManagerServiceInternal +import com.android.server.testutils.mock +import com.android.server.testutils.nullable +import com.android.server.testutils.whenever +import org.junit.Assert +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement +import org.mockito.AdditionalMatchers.or +import org.mockito.invocation.InvocationOnMock +import org.mockito.quality.Strictness +import java.io.File +import java.io.IOException +import java.nio.file.Files +import java.security.PublicKey +import java.security.cert.CertificateException +import java.util.Arrays +import java.util.Random +import java.util.concurrent.FutureTask + +/** + * A utility for mocking behavior of the system and dependencies when testing PackageManagerService + * + * Create one of these and call [stageNominalSystemState] as a basis for additional behavior in most + * tests. + */ +class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { + private val random = Random() + val mocks = Mocks() + val packageCacheDirectory: File = + Files.createTempDirectory("packageCache").toFile() + val rootDirectory: File = + Files.createTempDirectory("root").toFile() + val dataAppDirectory: File = + File(Files.createTempDirectory("data").toFile(), "app") + val frameworkSignature: SigningDetails = SigningDetails(arrayOf(generateSpySignature()), 3) + val systemPartitions: List<PackageManagerService.ScanPartition> = + redirectScanPartitions(PackageManagerService.SYSTEM_PARTITIONS) + val session: StaticMockitoSession + + /** Tracks temporary files created by this class during the running of a test. */ + private val createdFiles = ArrayList<File>() + + /** Settings that are expected to be added as part of the test */ + private val mPendingPackageAdds: MutableList<Pair<String, PackageSetting>> = ArrayList() + + /** Settings simulated to be stored on disk */ + private val mPreExistingSettings = ArrayMap<String, PackageSetting>() + + /** The active map simulating the in memory storage of Settings */ + private val mSettingsMap = ArrayMap<String, PackageSetting>() + + init { + val apply = ExtendedMockito.mockitoSession() + .strictness(Strictness.LENIENT) + .mockStatic(SystemProperties::class.java) + .mockStatic(SystemConfig::class.java) + .mockStatic(SELinuxMMAC::class.java) + .mockStatic(FallbackCategoryProvider::class.java) + .mockStatic(PackageManagerServiceUtils::class.java) + .mockStatic(Environment::class.java) + .mockStatic(SystemServerInitThreadPool::class.java) + .mockStatic(ParsingPackageUtils::class.java) + .mockStatic(LockGuard::class.java) + .mockStatic(EventLog::class.java) + .mockStatic(LocalServices::class.java) + .apply(withSession) + session = apply.startMocking() + whenever(mocks.settings.insertPackageSettingLPw( + any(PackageSetting::class.java), any(AndroidPackage::class.java))) { + val name: String = (getArgument<Any>(0) as PackageSetting).name + val pendingAdd = + mPendingPackageAdds.firstOrNull { it.first == name } ?: return@whenever null + mPendingPackageAdds.remove(pendingAdd) + mSettingsMap[name] = pendingAdd.second + null + } + whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(), + nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), + nullable(), nullable(), nullable())) { + val name: String = getArgument(0) + val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name } + ?: return@whenever null + mPendingPackageAdds.remove(pendingAdd) + mSettingsMap[name] = pendingAdd.second + pendingAdd.second + } + whenever(mocks.settings.packagesLocked).thenReturn(mSettingsMap) + whenever(mocks.settings.getPackageLPr(anyString())) { mSettingsMap[getArgument<Any>(0)] } + whenever(mocks.settings.readLPw(nullable())) { + mSettingsMap.putAll(mPreExistingSettings) + !mPreExistingSettings.isEmpty() + } + } + + /** Collection of mocks used for PackageManagerService tests. */ + + class Mocks { + val lock = Any() + val installLock = Any() + val injector: PackageManagerService.Injector = mock() + val systemWrapper: PackageManagerService.SystemWrapper = mock() + val context: Context = mock() + val userManagerService: UserManagerService = mock() + val componentResolver: ComponentResolver = mock() + val permissionManagerInternal: PermissionManagerServiceInternal = mock() + val incrementalManager: IncrementalManager = mock() + val platformCompat: PlatformCompat = mock() + val settings: Settings = mock() + val resources: Resources = mock() + val systemConfig: SystemConfig = mock() + val apexManager: ApexManager = mock() + val userManagerInternal: UserManagerInternal = mock() + val packageParser: PackageParser2 = mock() + val keySetManagerService: KeySetManagerService = mock() + val packageAbiHelper: PackageAbiHelper = mock() + val appsFilter: AppsFilter = mock() + val dexManager: DexManager = mock() + val installer: Installer = mock() + val displayMetrics: DisplayMetrics = mock() + val permissionManager: IPermissionManager = mock() + } + + companion object { + private const val DEVICE_PROVISIONING_PACKAGE_NAME = + "com.example.android.device.provisioning" + private val DEFAULT_AVAILABLE_FEATURES_MAP = ArrayMap<String, FeatureInfo>() + private val DEFAULT_ACTIVE_APEX_INFO_LIST = emptyList<ApexManager.ActiveApexInfo>() + private val DEFAULT_SHARED_LIBRARIES_LIST = + ArrayMap<String, SystemConfig.SharedLibraryEntry>() + private val DEFAULT_USERS = Arrays.asList( + UserInfo(UserHandle.USER_SYSTEM, "primary", "", + UserInfo.FLAG_PRIMARY or UserInfo.FLAG_SYSTEM or UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_SYSTEM)) + public val DEFAULT_VERSION_INFO = Settings.VersionInfo() + + init { + DEFAULT_VERSION_INFO.fingerprint = "abcdef" + DEFAULT_VERSION_INFO.sdkVersion = Build.VERSION_CODES.R + DEFAULT_VERSION_INFO.databaseVersion = Settings.CURRENT_DATABASE_VERSION + } + } + + /** + * Clean up any potentially dangling state. This should be run at the end of every test to + * account for changes to static memory, such as [LocalServices] + */ + fun cleanup() { + createdFiles.forEach(File::delete) + createdFiles.clear() + mSettingsMap.clear() + mPendingPackageAdds.clear() + mPreExistingSettings.clear() + session.finishMocking() + } + + /** + * Run this method to ensure that all expected actions were executed, such as pending + * [Settings] adds. + */ + fun validateFinalState() { + if (mPendingPackageAdds.isNotEmpty()) { + Assert.fail( + "Not all expected settings were added: ${mPendingPackageAdds.map { it.first }}") + } + } + + /** + * This method stages enough of system startup to execute the PackageManagerService constructor + * successfullly. + */ + @Throws(Exception::class) + fun stageNominalSystemState() { + whenever(mocks.injector.context).thenReturn(mocks.context) + whenever(mocks.injector.lock).thenReturn(mocks.lock) + whenever(mocks.injector.installLock).thenReturn(mocks.installLock) + whenever(mocks.injector.systemWrapper).thenReturn(mocks.systemWrapper) + whenever(mocks.injector.userManagerService).thenReturn(mocks.userManagerService) + whenever(mocks.injector.componentResolver).thenReturn(mocks.componentResolver) + whenever(mocks.injector.permissionManagerServiceInternal) { + mocks.permissionManagerInternal + } + whenever(mocks.injector.permissionManagerService).thenReturn(mocks.permissionManager) + whenever(mocks.injector.incrementalManager).thenReturn(mocks.incrementalManager) + whenever(mocks.injector.compatibility).thenReturn(mocks.platformCompat) + whenever(mocks.injector.settings).thenReturn(mocks.settings) + whenever(mocks.injector.dexManager).thenReturn(mocks.dexManager) + whenever(mocks.injector.systemConfig).thenReturn(mocks.systemConfig) + whenever(mocks.injector.apexManager).thenReturn(mocks.apexManager) + whenever(mocks.injector.scanningCachingPackageParser).thenReturn(mocks.packageParser) + whenever(mocks.injector.scanningPackageParser).thenReturn(mocks.packageParser) + whenever(mocks.injector.systemPartitions).thenReturn(systemPartitions) + whenever(mocks.injector.appsFilter).thenReturn(mocks.appsFilter) + whenever(mocks.injector.abiHelper).thenReturn(mocks.packageAbiHelper) + whenever(mocks.injector.userManagerInternal).thenReturn(mocks.userManagerInternal) + whenever(mocks.injector.installer).thenReturn(mocks.installer) + whenever(mocks.injector.displayMetrics).thenReturn(mocks.displayMetrics) + wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig) + whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP) + whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST) + wheneverStatic { SystemProperties.getBoolean("fw.free_cache_v2", true) }.thenReturn(true) + wheneverStatic { Environment.getPackageCacheDirectory() }.thenReturn(packageCacheDirectory) + wheneverStatic { SystemProperties.digestOf("ro.build.fingerprint") }.thenReturn("cacheName") + wheneverStatic { Environment.getRootDirectory() }.thenReturn(rootDirectory) + wheneverStatic { SystemServerInitThreadPool.submit(any(Runnable::class.java), anyString())} + .thenAnswer { FutureTask<Any?>(it.getArgument(0), null) } + + wheneverStatic { Environment.getDataDirectory() }.thenReturn(dataAppDirectory.parentFile) + wheneverStatic { Environment.getDataSystemDirectory() } + .thenReturn(File(dataAppDirectory.parentFile, "system")) + whenever(mocks.context.resources).thenReturn(mocks.resources) + whenever(mocks.resources.getString(R.string.config_deviceProvisioningPackage)) { + DEVICE_PROVISIONING_PACKAGE_NAME + } + whenever(mocks.apexManager.activeApexInfos).thenReturn(DEFAULT_ACTIVE_APEX_INFO_LIST) + whenever(mocks.settings.packagesLocked).thenReturn(mSettingsMap) + whenever(mocks.settings.internalVersion).thenReturn(DEFAULT_VERSION_INFO) + whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService) + whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService) + whenever(mocks.packageAbiHelper.derivePackageAbi( + any(AndroidPackage::class.java), anyBoolean(), nullable(), any(File::class.java))) { + android.util.Pair(PackageAbiHelper.Abis("", ""), + PackageAbiHelper.NativeLibraryPaths("", false, "", "")) + } + whenever(mocks.userManagerInternal.getUsers(true, false, false)).thenReturn(DEFAULT_USERS) + whenever(mocks.userManagerService.userIds).thenReturn(intArrayOf(0)) + whenever(mocks.userManagerService.exists(0)).thenReturn(true) + whenever(mocks.packageAbiHelper.deriveNativeLibraryPaths( + any(AndroidPackage::class.java), anyBoolean(), any(File::class.java))) { + PackageAbiHelper.NativeLibraryPaths("", false, "", "") + } + // everything visible by default + whenever(mocks.appsFilter.shouldFilterApplication( + anyInt(), nullable(), nullable(), anyInt())) { false } + + val displayManager: DisplayManager = mock() + whenever(mocks.context.getSystemService(DisplayManager::class.java)) + .thenReturn(displayManager) + val display: Display = mock() + whenever(displayManager.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(display) + + stageFrameworkScan() + stageInstallerScan() + stageServicesExtensionScan() + stageSystemSharedLibraryScan() + stagePermissionsControllerScan() + stageInstantAppResolverScan() + } + + /** + * This method will stage the parsing and scanning of a package as well as add it to the + * [PackageSetting]s read from disk. + */ + @Throws(Exception::class) + fun stageScanExistingPackage( + packageName: String, + versionCode: Long, + parent: File?, + withPackage: (PackageImpl) -> PackageImpl = { it }, + withSetting: + (PackageSettingBuilder) -> PackageSettingBuilder = { it }, + withExistingSetting: + (PackageSettingBuilder) -> PackageSettingBuilder = { it } + ) { + val existingSettingBuilderRef = arrayOfNulls<PackageSettingBuilder>(1) + stageScanNewPackage(packageName, versionCode, parent, withPackage, + withSetting = { settingBuilder -> + withSetting(settingBuilder) + existingSettingBuilderRef[0] = settingBuilder + settingBuilder + }) + existingSettingBuilderRef[0]?.setPackage(null) + val packageSetting = existingSettingBuilderRef[0]?.let { withExistingSetting(it) }!!.build() + addPreExistingSetting(packageName, packageSetting) + } + + /** + * This method will stage a [PackageSetting] read from disk, but does not stage any scanning + * or parsing of the package. + */ + fun addPreExistingSetting(packageName: String, packageSetting: PackageSetting) { + mPreExistingSettings[packageName] = packageSetting + } + + /** + * This method will stage the parsing and scanning of a package but will not add it to the set + * of [PackageSetting]s read from disk. + */ + @Throws(Exception::class) + fun stageScanNewPackage( + packageName: String, + versionCode: Long, + parent: File?, + withPackage: (PackageImpl) -> PackageImpl = { it }, + withSetting: (PackageSettingBuilder) -> PackageSettingBuilder = { it } + ) { + val pair = createBasicAndroidPackage(parent, packageName, versionCode) + val apkPath = pair.first + val pkg = withPackage(pair.second) + stageParse(apkPath, pkg) + val parentFile = apkPath.parentFile + val settingBuilder = withSetting(createBasicSettingBuilder(parentFile, pkg)) + stageSettingInsert(packageName, settingBuilder.build()) + } + + /** + * Creates a simple package that should reasonably parse for scan operations. This can be used + * as a basis for more complicated packages. + */ + fun createBasicAndroidPackage( + parent: File?, + packageName: String, + versionCode: Long, + signingDetails: SigningDetails = + createRandomSigningDetails() + ): Pair<File, PackageImpl> { + val apkPath = File(File(parent, packageName), "base.apk") + val pkg = PackageImpl.forTesting(packageName, apkPath.parentFile.path) as PackageImpl + pkg.signingDetails = signingDetails + wheneverStatic { ParsingPackageUtils.getSigningDetails(eq(pkg), anyBoolean()) } + .thenReturn(signingDetails) + pkg.versionCode = versionCode.toInt() + pkg.versionCodeMajor = (versionCode shr 32).toInt() + pkg.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT + return Pair(apkPath, pkg) + } + + /** + * This method will create a spy of a [SigningDetails] object to be used when simulating the + * collection of signatures. + */ + fun createRandomSigningDetails(): SigningDetails { + val signingDetails = spy(SigningDetails(arrayOf(generateSpySignature()), + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3)) + doReturn(true).whenever(signingDetails).checkCapability( + anyString(), anyInt()) + doReturn(true).whenever(signingDetails).checkCapability( + any(SigningDetails::class.java), anyInt()) + return signingDetails + } + + /** + * This method will create a basic [PackageSettingBuilder] from an [AndroidPackage] with all of + * the necessary parameters to be returned by a simple scan. This can be used as a basis for + * more complicated settings. + */ + fun createBasicSettingBuilder(parentFile: File, pkg: AndroidPackage): PackageSettingBuilder { + return createBasicSettingBuilder(parentFile, pkg.packageName, pkg.longVersionCode, + pkg.signingDetails) + .setPackage(pkg) + } + + /** + * This method will create a basic [PackageSettingBuilder] with all of the necessary parameters + * to be returned by a simple scan. This can be used as a basis for more complicated settings. + */ + fun createBasicSettingBuilder( + parentFile: File, + packageName: String, + versionCode: Long, + signingDetails: SigningDetails + ): PackageSettingBuilder { + return PackageSettingBuilder() + .setCodePath(parentFile.path) + .setName(packageName) + .setPVersionCode(versionCode) + .setSigningDetails(signingDetails) + } + + fun createBasicApplicationInfo(pkg: ParsingPackage): ApplicationInfo { + val applicationInfo: ApplicationInfo = mock() + applicationInfo.packageName = pkg.packageName + return applicationInfo + } + + fun createBasicActivityInfo( + pkg: ParsingPackage, + applicationInfo: ApplicationInfo?, + className: String? + ): + ActivityInfo { + val activityInfo = ActivityInfo() + activityInfo.applicationInfo = applicationInfo + activityInfo.packageName = pkg.packageName + activityInfo.name = className + return activityInfo + } + + fun createBasicServiceInfo( + pkg: ParsingPackage, + applicationInfo: ApplicationInfo?, + className: String? + ): + ServiceInfo { + val serviceInfo = ServiceInfo() + serviceInfo.applicationInfo = applicationInfo + serviceInfo.packageName = pkg.packageName + serviceInfo.name = className + return serviceInfo + } + + /** Finds the appropriate partition, if available, based on a scan flag unique to it. */ + fun getPartitionFromFlag(scanFlagMask: Int): PackageManagerService.ScanPartition = + systemPartitions.first { (it.scanFlag and scanFlagMask) != 0 } + + @Throws(Exception::class) + private fun stageParse(path: File, parseResult: ParsingPackage): ParsedPackage { + val basePath = path.parentFile + basePath.mkdirs() + path.createNewFile() + createdFiles.add(path) + val parsedPackage = parseResult.hideAsParsed() as ParsedPackage + whenever(mocks.packageParser.parsePackage( + or(eq(path), eq(basePath)), anyInt(), anyBoolean())) { parsedPackage } + return parsedPackage + } + + private fun stageSettingInsert(name: String, setting: PackageSetting): PackageSetting { + mPendingPackageAdds.add(Pair(name, setting)) + return setting + } + + @Throws(Exception::class) + private fun stageFrameworkScan() { + val apk = File(File(rootDirectory, "framework"), "framework-res.apk") + val frameworkPkg = PackageImpl.forTesting("android", + apk.parentFile.path) as PackageImpl + wheneverStatic { ParsingPackageUtils.getSigningDetails(frameworkPkg, true) } + .thenReturn(frameworkSignature) + stageParse(apk, frameworkPkg) + stageSettingInsert("android", + PackageSettingBuilder().setCodePath(apk.path).setName( + "android").setPackage(frameworkPkg).build()) + } + + @Throws(Exception::class) + private fun stageInstantAppResolverScan() { + whenever(mocks.resources.getStringArray(R.array.config_ephemeralResolverPackage)) { + arrayOf("com.android.test.ephemeral.resolver") + } + stageScanNewPackage("com.android.test.ephemeral.resolver", + 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder, + withPackage = { pkg: PackageImpl -> + val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) + whenever(applicationInfo.isPrivilegedApp).thenReturn(true) + mockQueryServices(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE, + createBasicServiceInfo(pkg, applicationInfo, "test.EphemeralService")) + mockQueryActivities(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS, + createBasicActivityInfo(pkg, applicationInfo, "test.SettingsActivity")) + pkg + }, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + }) + } + + @Throws(Exception::class) + private fun stagePermissionsControllerScan() { + stageScanNewPackage("com.android.permissions.controller", + 1L, systemPartitions[0].privAppFolder, + withPackage = { pkg: PackageImpl -> + val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) + whenever(applicationInfo.isPrivilegedApp).thenReturn(true) + mockQueryActivities(Intent.ACTION_MANAGE_PERMISSIONS, + createBasicActivityInfo( + pkg, applicationInfo, "test.PermissionActivity")) + pkg + }, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + }) + } + + @Throws(Exception::class) + private fun stageSystemSharedLibraryScan() { + stageScanNewPackage("android.ext.shared", + 1L, systemPartitions[0].appFolder, + withPackage = { it.addLibraryName("android.ext.shared") as PackageImpl }, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + } + ) + } + + @Throws(Exception::class) + private fun stageServicesExtensionScan() { + whenever(mocks.context.getString(R.string.config_servicesExtensionPackage)) { + "com.android.test.services.extension" + } + stageScanNewPackage("com.android.test.services.extension", + 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_SYSTEM_EXT).privAppFolder, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + }) + } + + @Throws(Exception::class) + private fun stageInstallerScan() { + stageScanNewPackage( + "com.android.test.installer", + 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder, + withPackage = { pkg: PackageImpl -> + val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) + whenever(applicationInfo.isPrivilegedApp).thenReturn(true) + val installerActivity: ActivityInfo = createBasicActivityInfo( + pkg, applicationInfo, "test.InstallerActivity") + mockQueryActivities(Intent.ACTION_INSTALL_PACKAGE, installerActivity) + mockQueryActivities(Intent.ACTION_UNINSTALL_PACKAGE, installerActivity) + mockQueryActivities(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE, + installerActivity) + pkg + }, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + } + ) + } + + private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) { + whenever(mocks.componentResolver.queryActivities( + argThat { intent: Intent? -> intent != null && (action == intent.action) }, + nullable(), anyInt(), anyInt())) { + ArrayList(activities.asList().map { info: ActivityInfo? -> + ResolveInfo().apply { activityInfo = info } + }) + } + } + + private fun mockQueryServices(action: String, vararg services: ServiceInfo) { + whenever(mocks.componentResolver.queryServices( + argThat { intent: Intent? -> intent != null && (action == intent.action) }, + nullable(), anyInt(), anyInt())) { + ArrayList(services.asList().map { info -> + ResolveInfo().apply { serviceInfo = info } + }) + } + } + + fun generateSpySignature(): Signature { + val bytes = ByteArray(32) + random.nextBytes(bytes) + val signature = spy(Signature(bytes)) + try { + val mockPublicKey: PublicKey = mock() + doReturn(mockPublicKey).whenever(signature).getPublicKey() + } catch (e: CertificateException) { + throw RuntimeException(e) + } + return signature + } + + /** Override get*Folder methods to point to temporary local directories */ + + @Throws(IOException::class) + private fun redirectScanPartitions(partitions: List<PackageManagerService.ScanPartition>): + List<PackageManagerService.ScanPartition> { + val spiedPartitions: MutableList<PackageManagerService.ScanPartition> = + ArrayList(partitions.size) + for (partition: PackageManagerService.ScanPartition in partitions) { + val spy = spy(partition) + val newRoot = Files.createTempDirectory(partition.folder.name).toFile() + whenever(spy.overlayFolder).thenReturn(File(newRoot, "overlay")) + whenever(spy.appFolder).thenReturn(File(newRoot, "app")) + whenever(spy.privAppFolder).thenReturn(File(newRoot, "priv-app")) + whenever(spy.folder).thenReturn(newRoot) + spiedPartitions.add(spy) + } + return spiedPartitions + } +} + +/** + * Sets up a basic [MockSystem] for use in a test method. This will create a MockSystem before the + * test method and any [org.junit.Before] annotated methods. It can then be used to access the + * MockSystem via the [system] method or the mocks directly via [mocks]. + */ +class MockSystemRule : TestRule { + var mockSystem: MockSystem? = null + override fun apply(base: Statement?, description: Description?) = object : Statement() { + @Throws(Throwable::class) + override fun evaluate() { + mockSystem = MockSystem() + try { + base!!.evaluate() + } finally { + mockSystem?.cleanup() + mockSystem = null + } + } + } + + /** Fetch the [MockSystem] instance prepared for this test */ + fun system(): MockSystem = mockSystem!! + /** Fetch the [MockSystem.Mocks] prepared for this test */ + fun mocks(): MockSystem.Mocks = mockSystem!!.mocks +} diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt new file mode 100644 index 000000000000..bd44c360b518 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt @@ -0,0 +1,173 @@ +/* + * 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.pm + +import android.content.pm.ApplicationInfo.FLAG_SYSTEM +import android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED +import android.content.pm.PackageManager +import android.content.pm.PackageParser +import android.os.Build +import android.os.Process +import android.util.Log +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.testutils.whenever +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.notNullValue +import org.hamcrest.collection.IsMapContaining.hasKey +import org.hamcrest.core.IsNot.not +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.argThat +import org.mockito.Mockito +import org.mockito.Mockito.verify +import java.io.File + +@RunWith(JUnit4::class) +class PackageManagerServiceBootTest { + + @Rule + @JvmField + val rule = MockSystemRule() + + @Before + @Throws(Exception::class) + fun setup() { + Log.i("system.out", "setup", Exception()) + rule.system().stageNominalSystemState() + } + + private fun createPackageManagerService(): PackageManagerService { + return PackageManagerService(rule.mocks().injector, + false /*coreOnly*/, + false /*factoryTest*/, + MockSystem.DEFAULT_VERSION_INFO.fingerprint, + false /*isEngBuild*/, + false /*isUserDebugBuild*/, + Build.VERSION_CODES.CUR_DEVELOPMENT, + Build.VERSION.INCREMENTAL) + } + + @Test + @Throws(Exception::class) + fun simpleConstruction() { + val pm = createPackageManagerService() + verify(rule.mocks().injector).bootstrap(pm) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.system", + Process.SYSTEM_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.phone", + Process.PHONE_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.log", + Process.LOG_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.nfc", + Process.NFC_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.bluetooth", + Process.BLUETOOTH_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.shell", + Process.SHELL_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.se", + Process.SE_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.networkstack", + Process.NETWORK_STACK_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + rule.system().validateFinalState() + } + + @Test + @Throws(Exception::class) + fun existingDataPackage_remains() { + rule.system().stageScanExistingPackage("a.data.package", 1L, rule.system().dataAppDirectory) + val pm = createPackageManagerService() + rule.system().validateFinalState() + assertThat(pm.mPackages, hasKey("a.data.package")) + } + + @Test + @Throws(Exception::class) + fun unexpectedDataPackage_isRemoved() { + rule.system().stageScanNewPackage( + "a.data.package", 1L, rule.system().dataAppDirectory) + val pm = createPackageManagerService() + verify(rule.mocks().settings, Mockito.never()).insertPackageSettingLPw( + argThat { setting: PackageSetting -> setting.name == "a.data.package" }, + argThat { pkg: AndroidPackage -> pkg.packageName == "a.data.package" }) + assertThat(pm.mPackages, not(hasKey("a.data.package"))) + } + + @Test + @Throws(Exception::class) + fun expectedPackageMissing_doesNotReplace() { + // setup existing package + rule.system().stageScanExistingPackage("a.data.package", 1L, + rule.system().dataAppDirectory) + // simulate parsing failure for any path containing the package name. + whenever(rule.mocks().packageParser.parsePackage( + argThat { path: File -> path.path.contains("a.data.package") }, + anyInt(), + anyBoolean())) + .thenThrow(PackageParser.PackageParserException( + PackageManager.INSTALL_FAILED_INVALID_APK, "Oh no!")) + val pm = createPackageManagerService() + verify(rule.mocks().settings, Mockito.never()).insertPackageSettingLPw( + argThat { setting: PackageSetting -> setting.name == "a.data.package" }, + argThat { pkg: AndroidPackage -> pkg.packageName == "a.data.package" }) + assertThat(pm.mPackages, not(hasKey("a.data.package"))) + } + + @Test + @Throws(Exception::class) + fun expectingBetter_updateStillBetter() { + // Debug.waitForDebugger() + val systemAppPackageName = "com.android.test.updated.system.app" + val systemAppSigningDetails = rule.system().createRandomSigningDetails() + val systemVersionParent = rule.system() + .getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder + + // system app v1 is disabled + whenever(rule.mocks().settings.isDisabledSystemPackageLPr(systemAppPackageName)) { + true + } + whenever(rule.mocks().settings.getDisabledSystemPkgLPr(systemAppPackageName)) { + rule.system().createBasicSettingBuilder( + File(systemVersionParent, systemAppPackageName), + systemAppPackageName, 1, systemAppSigningDetails).build() + } + + // system app v3 is on data/app + rule.system().stageScanExistingPackage( + systemAppPackageName, + 3, + rule.system().dataAppDirectory, + withPackage = { it.apply { signingDetails = systemAppSigningDetails } }, + withExistingSetting = { it.setPkgFlags(FLAG_SYSTEM) }) + + // system app v2 is scanned from system + rule.system().stageScanNewPackage(systemAppPackageName, 2, systemVersionParent, + withPackage = { it.apply { signingDetails = systemAppSigningDetails } }, + withSetting = { it.setPkgFlags(FLAG_SYSTEM) }) + + val pm = createPackageManagerService() + + assertThat("system package should exist after boot", + pm.mPackages[systemAppPackageName], notNullValue()) + assertThat("system package should remain at version on data/app", + pm.mPackages[systemAppPackageName]!!.longVersionCode, equalTo(3)) + } +}
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java new file mode 100644 index 000000000000..df533f3c122a --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java @@ -0,0 +1,197 @@ +/* + * 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.utils.quota; + +import static com.google.common.truth.Truth.assertThat; + +import android.testing.TestableContext; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; + +import java.time.Duration; + +@SmallTest +public class MultiRateLimiterTest { + + private static final int USER_ID = 1; + private static final String PACKAGE_NAME_1 = "com.android.package.one"; + private static final String PACKAGE_NAME_2 = "com.android.package.two"; + private static final String TAG = "tag"; + + @Rule + public final TestableContext mContext = + new TestableContext(InstrumentationRegistry.getContext(), null); + + private final InjectorForTest mInjector = new InjectorForTest(); + + private static class InjectorForTest extends QuotaTracker.Injector { + Duration mElapsedTime = Duration.ZERO; + + @Override + public long getElapsedRealtime() { + return mElapsedTime.toMillis(); + } + + @Override + public boolean isAlarmManagerReady() { + return true; + } + } + + @Test + public void testSingleRateLimit_belowLimit_isWithinQuota() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(3, Duration.ofSeconds(20)) + .build(); + + // Three quick events are within quota. + mInjector.mElapsedTime = Duration.ZERO; + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(50); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(100); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + } + + @Test + public void testSingleRateLimit_aboveLimit_isNotWithinQuota() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(3, Duration.ofSeconds(20)) + .build(); + + mInjector.mElapsedTime = Duration.ZERO; + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(50); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(100); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(150); + // We hit the limit, 4th event in under 20 seconds is not within quota. + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + } + + @Test + public void testSingleRateLimit_afterGoingAboveQuotaAndWaitingWindow_isBackWithinQuota() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(3, Duration.ofSeconds(20)) + .build(); + + mInjector.mElapsedTime = Duration.ZERO; + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(50); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(100); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(150); + // We hit the limit, 4th event in under 20 seconds is not within quota. + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + + mInjector.mElapsedTime = Duration.ofSeconds(21); + // 20 seconds have passed, we're again within quota. + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + } + + @Test + public void createMultipleRateLimits_testTheyLimitsAsExpected() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(3, Duration.ofSeconds(20)) // 1st limit + .addRateLimit(4, Duration.ofSeconds(40)) // 2nd limit + .addRateLimit(5, Duration.ofSeconds(60)) // 3rd limit + .build(); + + // Testing the 1st limit + mInjector.mElapsedTime = Duration.ZERO; + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(50); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(100); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(150); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + + mInjector.mElapsedTime = Duration.ofSeconds(21); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + // Testing the 2nd limit + mInjector.mElapsedTime = Duration.ofSeconds(35); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + + mInjector.mElapsedTime = Duration.ofSeconds(42); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + // Testing the 3rd limit. + mInjector.mElapsedTime = Duration.ofSeconds(43); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + + mInjector.mElapsedTime = Duration.ofSeconds(62); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + } + + @Test + public void createSingleRateLimit_testItLimitsOnlyGivenUptc() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(3, Duration.ofSeconds(20)) + .build(); + + mInjector.mElapsedTime = Duration.ZERO; + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(50); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(100); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(150); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + // Different userId - packageName - tag combination is still allowed. + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + + mInjector.mElapsedTime = Duration.ofSeconds(21); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + } +} diff --git a/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt b/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt new file mode 100644 index 000000000000..72ae77e48e25 --- /dev/null +++ b/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt @@ -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.server.extendedtestutils + +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.dx.mockito.inline.extended.MockedMethod +import com.android.dx.mockito.inline.extended.StaticCapableStubber +import org.mockito.stubbing.Answer + +fun <T> StaticCapableStubber.wheneverStatic(methodCall: MockedMethod<T>) { + this.`when`(methodCall) +} + +fun <T> wheneverStatic(mockedMethod: MockedMethod<T>) = object : CustomStaticStubber<T> { + override fun thenAnswer(answer: Answer<T>) { + ExtendedMockito.doAnswer(answer).wheneverStatic(mockedMethod) + } + + override fun thenReturn(value: T) { + ExtendedMockito.doReturn(value).wheneverStatic(mockedMethod) + } +} + +interface CustomStaticStubber<T> { + fun thenAnswer(answer: Answer<T>) + fun thenReturn(value: T) +} diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 343b156e443a..6daa381f526e 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -33,6 +33,7 @@ android_test { "androidx.test.ext.truth", "androidx.test.runner", "androidx.test.rules", + "platform-compat-test-rules", "mockito-target-minus-junit4", "platform-test-annotations", "ShortcutManagerTestUtils", @@ -111,6 +112,16 @@ android_test { } java_library { + name: "servicestests-core-utils", + srcs: [ + "src/com/android/server/pm/PackageSettingBuilder.java", + ], + static_libs: [ + "services.core", + ], +} + +java_library { name: "servicestests-utils", srcs: [ "utils/**/*.java", diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java deleted file mode 100644 index 33ea1d6f829e..000000000000 --- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java +++ /dev/null @@ -1,144 +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.server; - -import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; -import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; -import static android.net.INetd.FIREWALL_CHAIN_STANDBY; -import static android.net.INetd.FIREWALL_RULE_ALLOW; -import static android.net.INetd.FIREWALL_RULE_DENY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.util.DebugUtils.valueToString; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.net.NetworkPolicyManager; -import android.util.ArrayMap; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.function.BiFunction; - -/** - * Test class for {@link NetworkManagementInternal}. - * - * To run the tests, use - * - * runtest -c com.android.server.NetworkManagementInternalTest frameworks-services - * - * or the following steps: - * - * Build: m FrameworksServicesTests - * Install: adb install -r \ - * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk - * Run: adb shell am instrument -e class com.android.server.NetworkManagementInternalTest -w \ - * com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class NetworkManagementInternalTest { - private static final int TEST_UID = 111; - - private NetworkManagementService.Injector mInjector; - private NetworkManagementInternal mNmi; - - @Before - public void setUp() { - final NetworkManagementService service = new NetworkManagementService(); - mInjector = service.getInjector(); - mNmi = service.new LocalService(); - } - - @Test - public void testIsNetworkRestrictedForUid() { - // No firewall chains enabled - assertFalse(mNmi.isNetworkRestrictedForUid(TEST_UID)); - - // Restrict usage of mobile data in background - mInjector.setUidOnMeteredNetworkList(true, TEST_UID, true); - assertTrue("Should be true since mobile data usage is restricted", - mNmi.isNetworkRestrictedForUid(TEST_UID)); - mInjector.reset(); - - // Data saver is on and uid is not allowlisted - mInjector.setDataSaverMode(true); - mInjector.setUidOnMeteredNetworkList(false, TEST_UID, false); - assertTrue("Should be true since data saver is on and the uid is not whitelisted", - mNmi.isNetworkRestrictedForUid(TEST_UID)); - mInjector.reset(); - - // Data saver is on and uid is allowlisted - mInjector.setDataSaverMode(true); - mInjector.setUidOnMeteredNetworkList(false, TEST_UID, true); - assertFalse("Should be false since data saver is on and the uid is whitelisted", - mNmi.isNetworkRestrictedForUid(TEST_UID)); - mInjector.reset(); - - final ArrayMap<Integer, ArrayMap<Integer, Boolean>> expected = new ArrayMap<>(); - // Dozable chain - final ArrayMap<Integer, Boolean> isRestrictedForDozable = new ArrayMap<>(); - isRestrictedForDozable.put(FIREWALL_RULE_DEFAULT, true); - isRestrictedForDozable.put(FIREWALL_RULE_ALLOW, false); - isRestrictedForDozable.put(FIREWALL_RULE_DENY, true); - expected.put(FIREWALL_CHAIN_DOZABLE, isRestrictedForDozable); - // Powersaver chain - final ArrayMap<Integer, Boolean> isRestrictedForPowerSave = new ArrayMap<>(); - isRestrictedForPowerSave.put(FIREWALL_RULE_DEFAULT, true); - isRestrictedForPowerSave.put(FIREWALL_RULE_ALLOW, false); - isRestrictedForPowerSave.put(FIREWALL_RULE_DENY, true); - expected.put(FIREWALL_CHAIN_POWERSAVE, isRestrictedForPowerSave); - // Standby chain - final ArrayMap<Integer, Boolean> isRestrictedForStandby = new ArrayMap<>(); - isRestrictedForStandby.put(FIREWALL_RULE_DEFAULT, false); - isRestrictedForStandby.put(FIREWALL_RULE_ALLOW, false); - isRestrictedForStandby.put(FIREWALL_RULE_DENY, true); - expected.put(FIREWALL_CHAIN_STANDBY, isRestrictedForStandby); - - final int[] chains = { - FIREWALL_CHAIN_STANDBY, - FIREWALL_CHAIN_POWERSAVE, - FIREWALL_CHAIN_DOZABLE - }; - final int[] states = { - FIREWALL_RULE_ALLOW, - FIREWALL_RULE_DENY, - FIREWALL_RULE_DEFAULT - }; - BiFunction<Integer, Integer, String> errorMsg = (chain, state) -> { - return String.format("Unexpected value for chain: %s and state: %s", - valueToString(NetworkPolicyManager.class, "FIREWALL_CHAIN_", chain), - valueToString(NetworkPolicyManager.class, "FIREWALL_RULE_", state)); - }; - for (int chain : chains) { - final ArrayMap<Integer, Boolean> expectedValues = expected.get(chain); - mInjector.setFirewallChainState(chain, true); - for (int state : states) { - mInjector.setFirewallRule(chain, TEST_UID, state); - assertEquals(errorMsg.apply(chain, state), - expectedValues.get(state), mNmi.isNetworkRestrictedForUid(TEST_UID)); - } - mInjector.reset(); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java index e609adc2a067..ce3751abfed7 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java @@ -72,7 +72,7 @@ import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.devicepolicy.MockUtils; -import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; import com.google.android.collect.Lists; @@ -133,10 +133,10 @@ public class NetworkScoreServiceTest { @Mock private UnaryOperator<List<ScoredNetwork>> mScanResultsFilter; @Mock private WifiInfo mWifiInfo; @Mock private NetworkScoreService.ScoringServiceConnection mServiceConnection; - @Mock private PermissionManagerServiceInternal mPermissionManagerInternal; + @Mock private LegacyPermissionManagerInternal mPermissionManagerInternal; @Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor; - @Captor private - ArgumentCaptor<PermissionManagerServiceInternal.PackagesProvider> mPackagesProviderCaptor; + @Captor private ArgumentCaptor<LegacyPermissionManagerInternal.PackagesProvider> + mPackagesProviderCaptor; private ContentResolver mContentResolver; private NetworkScoreService mNetworkScoreService; @@ -165,7 +165,7 @@ public class NetworkScoreServiceTest { mHandlerThread = new HandlerThread("NetworkScoreServiceTest"); mHandlerThread.start(); LocalServices.addService( - PermissionManagerServiceInternal.class, mPermissionManagerInternal); + LegacyPermissionManagerInternal.class, mPermissionManagerInternal); mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager, networkScorerAppData -> mServiceConnection, mHandlerThread.getLooper()); WifiConfiguration configuration = new WifiConfiguration(); @@ -191,7 +191,7 @@ public class NetworkScoreServiceTest { @After public void tearDown() throws Exception { mHandlerThread.quitSafely(); - LocalServices.removeServiceForTest(PermissionManagerServiceInternal.class); + LocalServices.removeServiceForTest(LegacyPermissionManagerInternal.class); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 3f3d5e5106c5..839bc93ba00f 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -35,6 +35,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.res.Resources; import android.graphics.drawable.Icon; import android.os.IBinder; import android.os.UserHandle; @@ -94,6 +95,8 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { @Mock private IAccessibilityServiceClient mMockServiceClient; @Mock private WindowMagnificationManager mMockWindowMagnificationMgr; @Mock private MagnificationController mMockMagnificationController; + @Mock private Resources mMockResources; + private AccessibilityUserState mUserState; private MessageCapturingHandler mHandler = new MessageCapturingHandler(null); @@ -121,6 +124,9 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { mMockA11yDisplayListener, mMockMagnificationController); + mMockResources = mock(Resources.class); + when(mMockContext.getResources()).thenReturn(mMockResources); + final AccessibilityUserState userState = new AccessibilityUserState( mA11yms.getCurrentUserIdLocked(), mMockContext, mA11yms); mA11yms.mUserStates.put(mA11yms.getCurrentUserIdLocked(), userState); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java index fbfb0455bd46..81ca92cc3c8c 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -42,11 +42,16 @@ import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; import android.content.ComponentName; import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.testing.DexmakerShareClassLoaderRule; import android.util.ArraySet; +import androidx.test.InstrumentationRegistry; + +import com.android.internal.R; import com.android.internal.util.test.FakeSettingsProvider; import org.junit.After; @@ -90,8 +95,13 @@ public class AccessibilityUserStateTest { private AccessibilityUserState mUserState; + private int mFocusStrokeWidthDefaultValue; + private int mFocusColorDefaultValue; + @Before public void setUp() { + final Resources resources = InstrumentationRegistry.getContext().getResources(); + MockitoAnnotations.initMocks(this); FakeSettingsProvider.clearSettingsProvider(); mMockResolver = new MockContentResolver(); @@ -99,6 +109,11 @@ public class AccessibilityUserStateTest { when(mMockContext.getContentResolver()).thenReturn(mMockResolver); when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME); when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo); + when(mMockContext.getResources()).thenReturn(resources); + + mFocusStrokeWidthDefaultValue = + resources.getDimensionPixelSize(R.dimen.accessibility_focus_highlight_stroke_width); + mFocusColorDefaultValue = resources.getColor(R.color.accessibility_focus_highlight_color); mUserState = new AccessibilityUserState(USER_ID, mMockContext, mMockListener); } @@ -129,6 +144,7 @@ public class AccessibilityUserStateTest { mUserState.setUserNonInteractiveUiTimeoutLocked(30); mUserState.setUserInteractiveUiTimeoutLocked(30); mUserState.setMagnificationModeLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + mUserState.setFocusAppearanceLocked(20, Color.BLUE); mUserState.onSwitchToAnotherUserLocked(); @@ -150,6 +166,8 @@ public class AccessibilityUserStateTest { assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked()); assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, mUserState.getMagnificationModeLocked()); + assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked()); + assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked()); } @Test @@ -350,6 +368,21 @@ public class AccessibilityUserStateTest { mUserState.getMagnificationModeLocked()); } + @Test + public void setFocusAppearanceData_returnExpectedFocusAppearanceData() { + final int focusStrokeWidthValue = 100; + final int focusColorValue = Color.BLUE; + + assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked()); + assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked()); + + mUserState.setFocusAppearanceLocked(focusStrokeWidthValue, focusColorValue); + + assertEquals(focusStrokeWidthValue, mUserState.getFocusStrokeWidthLocked()); + assertEquals(focusColorValue, mUserState.getFocusColorLocked()); + + } + private int getSecureIntForUser(String key, int userId) { return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId); } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java index b929061a967e..f38def8c3c77 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java @@ -37,6 +37,7 @@ import com.android.server.appsearch.proto.SearchSpecProto; import com.android.server.appsearch.proto.StringIndexingConfig; import com.android.server.appsearch.proto.TermMatchType; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.junit.Before; @@ -46,9 +47,7 @@ import org.junit.rules.TemporaryFolder; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class AppSearchImplTest { @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); @@ -66,14 +65,15 @@ public class AppSearchImplTest { + VisibilityStore.SCHEMA_TYPE) .addProperty( new AppSearchSchema.PropertyConfig.Builder( - VisibilityStore.PLATFORM_HIDDEN_PROPERTY) + VisibilityStore.NOT_PLATFORM_SURFACEABLE_PROPERTY) .setDataType( AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) .setCardinality( AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) .build()) .build(); - mVisibilitySchemaProto = SchemaToProtoConverter.convert(visibilityAppSearchSchema); + mVisibilitySchemaProto = + SchemaToProtoConverter.toSchemaTypeConfigProto(visibilityAppSearchSchema); } /** @@ -340,9 +340,13 @@ public class AppSearchImplTest { @Test public void testOptimize() throws Exception { // Insert schema - Set<AppSearchSchema> schemas = - Collections.singleton(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false); + List<AppSearchSchema> schemas = + Collections.singletonList(new AppSearchSchema.Builder("type").build()); + mAppSearchImpl.setSchema( + "database", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Insert enough documents. for (int i = 0; @@ -351,7 +355,7 @@ public class AppSearchImplTest { + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) { GenericDocument document = - new GenericDocument.Builder("uri" + i, "type") + new GenericDocument.Builder<>("uri" + i, "type") .setNamespace("namespace") .build(); mAppSearchImpl.putDocument("database", document); @@ -392,13 +396,17 @@ public class AppSearchImplTest { SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery(""); // Insert schema - Set<AppSearchSchema> schemas = - Collections.singleton(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false); + List<AppSearchSchema> schemas = + Collections.singletonList(new AppSearchSchema.Builder("type").build()); + mAppSearchImpl.setSchema( + "database", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Insert document GenericDocument document = - new GenericDocument.Builder("uri", "type").setNamespace("namespace").build(); + new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build(); mAppSearchImpl.putDocument("database", document); // Rewrite SearchSpec @@ -413,20 +421,28 @@ public class AppSearchImplTest { SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery(""); // Insert schema - Set<AppSearchSchema> schemas = - Set.of( + List<AppSearchSchema> schemas = + ImmutableList.of( new AppSearchSchema.Builder("typeA").build(), new AppSearchSchema.Builder("typeB").build()); - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); - mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database2", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Insert documents GenericDocument document1 = - new GenericDocument.Builder("uri", "typeA").setNamespace("namespace").build(); + new GenericDocument.Builder<>("uri", "typeA").setNamespace("namespace").build(); mAppSearchImpl.putDocument("database1", document1); GenericDocument document2 = - new GenericDocument.Builder("uri", "typeB").setNamespace("namespace").build(); + new GenericDocument.Builder<>("uri", "typeB").setNamespace("namespace").build(); mAppSearchImpl.putDocument("database2", document2); // Rewrite SearchSpec @@ -477,10 +493,14 @@ public class AppSearchImplTest { @Test public void testSetSchema() throws Exception { - Set<AppSearchSchema> schemas = - Collections.singleton(new AppSearchSchema.Builder("Email").build()); + List<AppSearchSchema> schemas = + Collections.singletonList(new AppSearchSchema.Builder("Email").build()); // Set schema Email to AppSearch database1 - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Create expected schemaType proto. SchemaProto expectedProto = @@ -500,35 +520,47 @@ public class AppSearchImplTest { public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception { mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("schema1").build()), + Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), /*forceOverride=*/ false); - mAppSearchImpl.setVisibility("database", Set.of("schema1")); // "schema1" is platform hidden now - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .containsExactly("database/schema1"); // Add a new schema, and include the already-existing "schema1" mAppSearchImpl.setSchema( "database", - Set.of( + ImmutableList.of( new AppSearchSchema.Builder("schema1").build(), new AppSearchSchema.Builder("schema2").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), /*forceOverride=*/ false); // Check that "schema1" is still platform hidden, but "schema2" is the default platform // visible. - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .containsExactly("database/schema1"); } @Test public void testRemoveSchema() throws Exception { - Set<AppSearchSchema> schemas = new HashSet<>(); - schemas.add(new AppSearchSchema.Builder("Email").build()); - schemas.add(new AppSearchSchema.Builder("Document").build()); + List<AppSearchSchema> schemas = + ImmutableList.of( + new AppSearchSchema.Builder("Email").build(), + new AppSearchSchema.Builder("Document").build()); // Set schema Email and Document to AppSearch database1 - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Create expected schemaType proto. SchemaProto expectedProto = @@ -547,20 +579,27 @@ public class AppSearchImplTest { assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) .containsExactlyElementsIn(expectedTypes); - final Set<AppSearchSchema> finalSchemas = - Collections.singleton(new AppSearchSchema.Builder("Email").build()); + final List<AppSearchSchema> finalSchemas = + Collections.singletonList(new AppSearchSchema.Builder("Email").build()); // Check the incompatible error has been thrown. AppSearchException e = expectThrows( AppSearchException.class, () -> mAppSearchImpl.setSchema( - "database1", finalSchemas, /*forceOverride=*/ false)); + "database1", + finalSchemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false)); assertThat(e).hasMessageThat().contains("Schema is incompatible"); assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]"); // ForceOverride to delete. - mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/ true); + mAppSearchImpl.setSchema( + "database1", + finalSchemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ true); // Check Document schema is removed. expectedProto = @@ -579,13 +618,22 @@ public class AppSearchImplTest { @Test public void testRemoveSchema_differentDataBase() throws Exception { // Create schemas - Set<AppSearchSchema> schemas = new HashSet<>(); - schemas.add(new AppSearchSchema.Builder("Email").build()); - schemas.add(new AppSearchSchema.Builder("Document").build()); + List<AppSearchSchema> schemas = + ImmutableList.of( + new AppSearchSchema.Builder("Email").build(), + new AppSearchSchema.Builder("Document").build()); // Set schema Email and Document to AppSearch database1 and 2 - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); - mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database2", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Create expected schemaType proto. SchemaProto expectedProto = @@ -610,8 +658,12 @@ public class AppSearchImplTest { .containsExactlyElementsIn(expectedTypes); // Save only Email to database1 this time. - schemas = Collections.singleton(new AppSearchSchema.Builder("Email").build()); - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ true); + schemas = Collections.singletonList(new AppSearchSchema.Builder("Email").build()); + mAppSearchImpl.setSchema( + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ true); // Create expected schemaType list, database 1 should only contain Email but database 2 // remains in same. @@ -638,76 +690,82 @@ public class AppSearchImplTest { public void testRemoveSchema_removedFromVisibilityStore() throws Exception { mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("schema1").build()), + Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), /*forceOverride=*/ false); - mAppSearchImpl.setVisibility("database", Set.of("schema1")); // "schema1" is platform hidden now - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .containsExactly("database/schema1"); // Remove "schema1" by force overriding - mAppSearchImpl.setSchema("database", Collections.emptySet(), /*forceOverride=*/ true); + mAppSearchImpl.setSchema( + "database", + Collections.emptyList(), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ true); // Check that "schema1" is no longer considered platform hidden - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .isEmpty(); // Add "schema1" back, it gets default visibility settings which means it's not platform // hidden. mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("schema1").build()), + Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .isEmpty(); } @Test - public void testSetVisibility_defaultPlatformVisible() throws Exception { + public void testSetSchema_defaultPlatformVisible() throws Exception { mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("Schema").build()), + Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .isEmpty(); } @Test - public void testSetVisibility_platformHidden() throws Exception { + public void testSetSchema_platformHidden() throws Exception { mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("Schema").build()), + Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"), /*forceOverride=*/ false); - mAppSearchImpl.setVisibility("database", Set.of("Schema")); - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .containsExactly("database/Schema"); } @Test - public void testSetVisibility_unknownSchema() throws Exception { - mAppSearchImpl.setSchema( - "database", - Collections.singleton(new AppSearchSchema.Builder("Schema").build()), - /*forceOverride=*/ false); - - // We'll throw an exception if a client tries to set visibility on a schema we don't know - // about. - AppSearchException e = - expectThrows( - AppSearchException.class, - () -> mAppSearchImpl.setVisibility("database", Set.of("UnknownSchema"))); - assertThat(e).hasMessageThat().contains("Unknown schema(s)"); - } - - @Test public void testHasSchemaType() throws Exception { // Nothing exists yet assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isFalse(); mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("Schema").build()), + Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isTrue(); @@ -723,7 +781,8 @@ public class AppSearchImplTest { // Has database1 mAppSearchImpl.setSchema( "database1", - Collections.singleton(new AppSearchSchema.Builder("schema").build()), + Collections.singletonList(new AppSearchSchema.Builder("schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); assertThat(mAppSearchImpl.getDatabasesLocked()) .containsExactly(VisibilityStore.DATABASE_NAME, "database1"); @@ -731,7 +790,8 @@ public class AppSearchImplTest { // Has both databases mAppSearchImpl.setSchema( "database2", - Collections.singleton(new AppSearchSchema.Builder("schema").build()), + Collections.singletonList(new AppSearchSchema.Builder("schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); assertThat(mAppSearchImpl.getDatabasesLocked()) .containsExactly(VisibilityStore.DATABASE_NAME, "database1", "database2"); diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java index dfe2de6538a4..a1f575a8720e 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java @@ -41,34 +41,19 @@ public class VisibilityStoreTest { @Test public void testSetVisibility() throws Exception { mVisibilityStore.setVisibility( - "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2")); - assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")) + "database", /*schemasNotPlatformSurfaceable=*/ Set.of("schema1", "schema2")); + assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database")) .containsExactly("schema1", "schema2"); // New .setVisibility() call completely overrides previous visibility settings. So // "schema1" isn't preserved. mVisibilityStore.setVisibility( - "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema3")); - assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")) + "database", /*schemasNotPlatformSurfaceable=*/ Set.of("schema1", "schema3")); + assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database")) .containsExactly("schema1", "schema3"); mVisibilityStore.setVisibility( - "database", /*platformHiddenSchemas=*/ Collections.emptySet()); - assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty(); - } - - @Test - public void testRemoveSchemas() throws Exception { - mVisibilityStore.setVisibility( - "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2")); - - // Removed just schema1 - mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema1")); - assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")) - .containsExactly("schema2"); - - // Removed everything now - mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema2")); - assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty(); + "database", /*schemasNotPlatformSurfaceable=*/ Collections.emptySet()); + assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database")).isEmpty(); } } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java index 98392a71be58..194be3761903 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java @@ -94,20 +94,24 @@ public class GenericDocumentToProtoConverterTest { PropertyProto.newBuilder() .setName("documentKey1") .addDocumentValues( - GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_1))); + GenericDocumentToProtoConverter.toDocumentProto( + DOCUMENT_PROPERTIES_1))); propertyProtoMap.put( "documentKey2", PropertyProto.newBuilder() .setName("documentKey2") .addDocumentValues( - GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_2))); + GenericDocumentToProtoConverter.toDocumentProto( + DOCUMENT_PROPERTIES_2))); List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet()); Collections.sort(sortedKey); for (String key : sortedKey) { documentProtoBuilder.addProperties(propertyProtoMap.get(key)); } DocumentProto documentProto = documentProtoBuilder.build(); - assertThat(GenericDocumentToProtoConverter.convert(document)).isEqualTo(documentProto); - assertThat(document).isEqualTo(GenericDocumentToProtoConverter.convert(documentProto)); + assertThat(GenericDocumentToProtoConverter.toDocumentProto(document)) + .isEqualTo(documentProto); + assertThat(document) + .isEqualTo(GenericDocumentToProtoConverter.toGenericDocument(documentProto)); } } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java index dedfca42ff90..88edcb857aaf 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java @@ -89,7 +89,10 @@ public class SchemaToProtoConverterTest { TermMatchType.Code.PREFIX))) .build(); - assertThat(SchemaToProtoConverter.convert(emailSchema)).isEqualTo(expectedEmailProto); + assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(emailSchema)) + .isEqualTo(expectedEmailProto); + assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedEmailProto)) + .isEqualTo(emailSchema); } @Test @@ -151,7 +154,9 @@ public class SchemaToProtoConverterTest { TermMatchType.Code.UNKNOWN))) .build(); - assertThat(SchemaToProtoConverter.convert(musicRecordingSchema)) + assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(musicRecordingSchema)) .isEqualTo(expectedMusicRecordingProto); + assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedMusicRecordingProto)) + .isEqualTo(musicRecordingSchema); } } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java index 518f53205588..7c68c6be4883 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java @@ -83,7 +83,7 @@ public class SnippetTest { // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = - SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage(searchResultProto); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match = result.getMatches().get(0); assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); @@ -131,7 +131,7 @@ public class SnippetTest { SearchResultProto.newBuilder().addResults(resultProto).build(); SearchResultPage searchResultPage = - SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage(searchResultProto); for (SearchResult result : searchResultPage.getResults()) { assertThat(result.getMatches()).isEmpty(); } @@ -196,7 +196,7 @@ public class SnippetTest { // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = - SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage(searchResultProto); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match1 = result.getMatches().get(0); diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java index 444155d12b3f..738527e1df0b 100644 --- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java @@ -18,14 +18,17 @@ package com.android.server.backup.utils; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.backup.BackupManager.OperationType; +import android.compat.testing.PlatformCompatChangeRule; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.Property; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.Signature; @@ -38,9 +41,15 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.backup.UserBackupManagerService; +import com.android.server.pm.parsing.pkg.AndroidPackage; + +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -50,17 +59,19 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class BackupEligibilityRulesTest { private static final String CUSTOM_BACKUP_AGENT_NAME = "custom.backup.agent"; - private static final String TEST_PACKAGE_NAME = "test_package"; + private static final String TEST_PACKAGE_NAME = "com.android.frameworks.servicestests"; private static final Signature SIGNATURE_1 = generateSignature((byte) 1); private static final Signature SIGNATURE_2 = generateSignature((byte) 2); private static final Signature SIGNATURE_3 = generateSignature((byte) 3); private static final Signature SIGNATURE_4 = generateSignature((byte) 4); + @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); + @Mock private PackageManagerInternal mMockPackageManagerInternal; @Mock private PackageManager mPackageManager; - private BackupEligibilityRules mBackupEligibilityRules; + private BackupEligibilityRules mBackupEligibilityRules; private int mUserId; @Before @@ -225,7 +236,6 @@ public class BackupEligibilityRulesTest { throws Exception { ApplicationInfo applicationInfo = getApplicationInfo(Process.SYSTEM_UID, /* flags */ 0, CUSTOM_BACKUP_AGENT_NAME); - BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( OperationType.MIGRATION); boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); @@ -234,6 +244,82 @@ public class BackupEligibilityRulesTest { } @Test + @EnableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP}) + public void appIsEligibleForBackup_adbBackupNotAllowed_returnsFalseForAdbBackup() + throws Exception { + ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID, + /* flags */ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, CUSTOM_BACKUP_AGENT_NAME); + BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( + OperationType.ADB_BACKUP); + when(mPackageManager.getProperty(eq(PackageManager.PROPERTY_ALLOW_ADB_BACKUP), + eq(TEST_PACKAGE_NAME))).thenReturn(getAdbBackupProperty( + /* allowAdbBackup */ false)); + + boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); + + assertThat(isEligible).isFalse(); + } + + @Test + @EnableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP}) + public void appIsEligibleForBackup_adbBackupAllowed_returnsTrueForAdbBackup() + throws Exception { + ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID, + /* flags */ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, CUSTOM_BACKUP_AGENT_NAME); + BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( + OperationType.ADB_BACKUP); + when(mPackageManager.getProperty(eq(PackageManager.PROPERTY_ALLOW_ADB_BACKUP), + eq(TEST_PACKAGE_NAME))).thenReturn(getAdbBackupProperty( + /* allowAdbBackup */ true)); + + boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); + + assertThat(isEligible).isTrue(); + } + + @Test + @EnableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP}) + public void appIsEligibleForBackup_debuggableNonPrivilegedApp_returnsTrueForAdbBackup() + throws Exception { + ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID, + /* flags */ ApplicationInfo.FLAG_DEBUGGABLE, CUSTOM_BACKUP_AGENT_NAME); + BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( + OperationType.ADB_BACKUP); + + boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); + + assertThat(isEligible).isTrue(); + } + + @Test + @DisableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP}) + public void appIsEligibleForBackup_allowBackupTrueBeforeS_returnsTrueForAdbBackup() + throws Exception { + ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID, + ApplicationInfo.FLAG_ALLOW_BACKUP, CUSTOM_BACKUP_AGENT_NAME); + BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( + OperationType.ADB_BACKUP); + + boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); + + assertThat(isEligible).isTrue(); + } + + @Test + @DisableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP}) + public void appIsEligibleForBackup_allowBackupFalseBeforeS_returnsFalseForAdbBackup() + throws Exception { + ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID, + /* flags */ 0, CUSTOM_BACKUP_AGENT_NAME); + BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( + OperationType.ADB_BACKUP); + + boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); + + assertThat(isEligible).isFalse(); + } + + @Test public void appIsDisabled_stateDefaultManifestEnabled_returnsFalse() throws Exception { ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.flags = 0; @@ -789,10 +875,15 @@ public class BackupEligibilityRulesTest { private static ApplicationInfo getApplicationInfo(int appUid, int flags, String backupAgentName) { ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.flags = 0; + applicationInfo.flags = flags; applicationInfo.packageName = TEST_PACKAGE_NAME; applicationInfo.uid = appUid; applicationInfo.backupAgentName = backupAgentName; return applicationInfo; } + + private static Property getAdbBackupProperty(boolean allowAdbBackup) { + return new Property(PackageManager.PROPERTY_ALLOW_ADB_BACKUP, allowAdbBackup, + TEST_PACKAGE_NAME, /* className */ ""); + } } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index cb5ca04c1fde..603608b23172 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -40,6 +40,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; import android.database.ContentObserver; +import android.hardware.display.DisplayManager; import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; @@ -402,7 +403,7 @@ public class DisplayModeDirectorTest { director.injectVotesByDisplay(votesByDisplay); assertThat(director.getModeSwitchingType()) - .isNotEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE); + .isNotEqualTo(DisplayManager.SWITCHING_TYPE_NONE); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30); @@ -411,9 +412,9 @@ public class DisplayModeDirectorTest { assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); assertThat(desiredSpecs.baseModeId).isEqualTo(30); - director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_NONE); + director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_NONE); assertThat(director.getModeSwitchingType()) - .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE); + .isEqualTo(DisplayManager.SWITCHING_TYPE_NONE); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30); @@ -428,9 +429,9 @@ public class DisplayModeDirectorTest { final int displayId = 0; DisplayModeDirector director = createDirectorFromFpsRange(0, 90); - director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS); + director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS); assertThat(director.getModeSwitchingType()) - .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS); + .isEqualTo(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.allowGroupSwitching).isFalse(); } @@ -440,9 +441,9 @@ public class DisplayModeDirectorTest { final int displayId = 0; DisplayModeDirector director = createDirectorFromFpsRange(0, 90); - director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); + director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); assertThat(director.getModeSwitchingType()) - .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); + .isEqualTo(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.allowGroupSwitching).isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java index 6debc893ea1b..f2254a98a70e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java @@ -98,17 +98,17 @@ public class ActiveSourceActionTest { } @Override - PowerManager getPowerManager() { + protected PowerManager getPowerManager() { return powerManager; } @Override - void writeStringSystemProperty(String key, String value) { + protected void writeStringSystemProperty(String key, String value) { // do nothing } @Override - HdmiCecConfig getHdmiCecConfig() { + protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java index a19336eeb5ea..6e4d994bd416 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java @@ -90,7 +90,7 @@ public class ArcInitiationActionFromAvrTest { } @Override - PowerManager getPowerManager() { + protected PowerManager getPowerManager() { return powerManager; } @@ -110,7 +110,7 @@ public class ArcInitiationActionFromAvrTest { } @Override - HdmiCecConfig getHdmiCecConfig() { + protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java index cd6977524943..bbe1156c5d61 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java @@ -86,7 +86,7 @@ public class ArcTerminationActionFromAvrTest { } @Override - PowerManager getPowerManager() { + protected PowerManager getPowerManager() { return powerManager; } @@ -111,7 +111,7 @@ public class ArcTerminationActionFromAvrTest { } @Override - HdmiCecConfig getHdmiCecConfig() { + protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java new file mode 100644 index 000000000000..af119c8c4267 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java @@ -0,0 +1,174 @@ +/* + * 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.server.hdmi; + +import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; +import static com.android.server.hdmi.Constants.ADDR_TV; +import static com.android.server.hdmi.Constants.PATH_RELATIONSHIP_ANCESTOR; +import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.ContextWrapper; +import android.hardware.hdmi.HdmiPortInfo; +import android.hardware.tv.cec.V1_0.SendMessageResult; +import android.os.Handler; +import android.os.IPowerManager; +import android.os.IThermalService; +import android.os.Looper; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; +import android.stats.hdmi.nano.HdmiStatsEnums; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import com.android.server.SystemService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +/** + * Tests for the {@link HdmiCecAtomWriter} class and its usage by the HDMI-CEC framework. + */ +@SmallTest +@Presubmit +@RunWith(JUnit4.class) +public class HdmiCecAtomLoggingTest { + private HdmiCecAtomWriter mHdmiCecAtomWriterSpy; + private HdmiControlService mHdmiControlServiceSpy; + private HdmiCecController mHdmiCecController; + private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback; + private HdmiMhlControllerStub mHdmiMhlControllerStub; + private FakeNativeWrapper mNativeWrapper; + private HdmiCecNetwork mHdmiCecNetwork; + private Looper mLooper; + private TestLooper mTestLooper = new TestLooper(); + private int mPhysicalAddress = 0x1110; + private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); + private HdmiPortInfo[] mHdmiPortInfo; + + @Mock private IPowerManager mIPowerManagerMock; + @Mock private IThermalService mIThermalServiceMock; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + + mHdmiCecAtomWriterSpy = spy(new HdmiCecAtomWriter()); + + mLooper = mTestLooper.getLooper(); + + Context mContextSpy = spy(new ContextWrapper( + InstrumentationRegistry.getInstrumentation().getTargetContext())); + + PowerManager powerManager = new PowerManager( + mContextSpy, mIPowerManagerMock, mIThermalServiceMock, new Handler(mLooper)); + doReturn(powerManager).when(mContextSpy).getSystemService(Context.POWER_SERVICE); + doReturn(true).when(mIPowerManagerMock).isInteractive(); + + mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy)); + doNothing().when(mHdmiControlServiceSpy) + .writeStringSystemProperty(anyString(), anyString()); + doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter(); + + HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy); + doReturn(hdmiCecConfig).when(mHdmiControlServiceSpy).getHdmiCecConfig(); + + mHdmiControlServiceSpy.setIoLooper(mLooper); + mHdmiControlServiceSpy.setMessageValidator( + new HdmiCecMessageValidator(mHdmiControlServiceSpy)); + mHdmiControlServiceSpy.setCecMessageBuffer( + new CecMessageBuffer(mHdmiControlServiceSpy)); + + mNativeWrapper = new FakeNativeWrapper(); + mNativeWrapper.setPhysicalAddress(mPhysicalAddress); + + mHdmiCecController = HdmiCecController.createWithNativeWrapper( + mHdmiControlServiceSpy, mNativeWrapper, mHdmiCecAtomWriterSpy); + mHdmiControlServiceSpy.setCecController(mHdmiCecController); + + mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlServiceSpy); + mHdmiControlServiceSpy.setHdmiMhlController( + mHdmiMhlControllerStub); + + mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlServiceSpy, + mHdmiCecController, mHdmiMhlControllerStub); + mHdmiControlServiceSpy.setHdmiCecNetwork(mHdmiCecNetwork); + + HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1]; + hdmiPortInfos[0] = + new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false); + mNativeWrapper.setPortInfo(hdmiPortInfos); + mNativeWrapper.setPortConnectionStatus(1, true); + + mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlServiceSpy); + mHdmiCecLocalDevicePlayback.init(); + mLocalDevices.add(mHdmiCecLocalDevicePlayback); + + mHdmiControlServiceSpy.initService(); + mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + + mTestLooper.dispatchAll(); + } + + @Test + public void testActiveSourceChanged_calledOnSetActiveSource() { + mHdmiControlServiceSpy.setActiveSource(1, 0x1111, "caller"); + verify(mHdmiCecAtomWriterSpy, times(1)) + .activeSourceChanged(1, 0x1111, PATH_RELATIONSHIP_ANCESTOR); + } + + @Test + public void testMessageReported_calledOnOutgoingMessage() { + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, + mPhysicalAddress); + + mHdmiCecController.sendCommand(message); + + verify(mHdmiCecAtomWriterSpy, times(1)).messageReported( + message, + HdmiStatsEnums.OUTGOING, + SendMessageResult.SUCCESS); + } + + @Test + public void testMessageReported_calledOnIncomingMessage() { + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + + mNativeWrapper.onCecMessage(message); + mTestLooper.dispatchAll(); + + verify(mHdmiCecAtomWriterSpy, times(1)).messageReported( + message, + HdmiStatsEnums.INCOMING); + } +} diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java index d10e075c5136..777713e4ed5a 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java @@ -99,7 +99,7 @@ public final class HdmiCecConfigTest { + "</cec-settings>", null); assertThat(hdmiCecConfig.getAllSettings()) .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); } @Test @@ -147,7 +147,7 @@ public final class HdmiCecConfigTest { + "</cec-settings>", null); assertThat(hdmiCecConfig.getUserSettings()) .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); } @Test @@ -230,7 +230,7 @@ public final class HdmiCecConfigTest { + " </setting>" + "</cec-settings>", null); assertTrue(hdmiCecConfig.isStringValueType( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP)); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)); } @Test @@ -330,10 +330,10 @@ public final class HdmiCecConfigTest { + " </setting>" + "</cec-settings>", null); assertThat(hdmiCecConfig.getAllowedStringValues( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP)) - .containsExactly(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)) + .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV, + HdmiControlManager.POWER_CONTROL_MODE_BROADCAST, + HdmiControlManager.POWER_CONTROL_MODE_NONE); } @Test @@ -374,7 +374,7 @@ public final class HdmiCecConfigTest { + "</cec-settings>", null); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getAllowedIntValues( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP)); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)); } @Test @@ -479,8 +479,8 @@ public final class HdmiCecConfigTest { + " </setting>" + "</cec-settings>", null); assertThat(hdmiCecConfig.getDefaultStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP)) - .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)) + .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV); } @Test @@ -521,7 +521,7 @@ public final class HdmiCecConfigTest { + "</cec-settings>", null); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getDefaultIntValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP)); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)); } @Test @@ -610,8 +610,8 @@ public final class HdmiCecConfigTest { public void getStringValue_GlobalSetting_BasicSanity() { when(mStorageAdapter.retrieveGlobalSetting( Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV)) - .thenReturn(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + HdmiControlManager.POWER_CONTROL_MODE_TV)) + .thenReturn(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" @@ -628,8 +628,8 @@ public final class HdmiCecConfigTest { + " </setting>" + "</cec-settings>", null); assertThat(hdmiCecConfig.getStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP)) - .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)) + .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); } @Test @@ -696,7 +696,7 @@ public final class HdmiCecConfigTest { + "</cec-settings>", null); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getIntValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP)); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)); } @Test @@ -812,8 +812,8 @@ public final class HdmiCecConfigTest { + "</cec-settings>", null); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST)); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)); } @Test @@ -835,7 +835,7 @@ public final class HdmiCecConfigTest { + "</cec-settings>", null); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, "bar")); } @@ -856,11 +856,11 @@ public final class HdmiCecConfigTest { + " <default-value string-value=\"to_tv\" />" + " </setting>" + "</cec-settings>", null); - hdmiCecConfig.setStringValue(HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + hdmiCecConfig.setStringValue(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); verify(mStorageAdapter).storeGlobalSetting( Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); } @Test diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java index 3fd3ce3cd0df..6bb68da2a894 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java @@ -170,7 +170,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Override - void writeStringSystemProperty(String key, String value) { + protected void writeStringSystemProperty(String key, String value) { // do nothing } @@ -185,12 +185,12 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Override - PowerManager getPowerManager() { + protected PowerManager getPowerManager() { return powerManager; } @Override - HdmiCecConfig getHdmiCecConfig() { + protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index dfeed1362b81..f7d52b6e8b8c 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -18,7 +18,6 @@ package com.android.server.hdmi; import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_INVALID; -import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; @@ -114,7 +113,7 @@ public class HdmiCecLocalDevicePlaybackTest { } @Override - void writeStringSystemProperty(String key, String value) { + protected void writeStringSystemProperty(String key, String value) { // do nothing } @@ -124,12 +123,12 @@ public class HdmiCecLocalDevicePlaybackTest { } @Override - PowerManager getPowerManager() { + protected PowerManager getPowerManager() { return powerManager; } @Override - HdmiCecConfig getHdmiCecConfig() { + protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } }; @@ -557,8 +556,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void handleOnStandby_ScreenOff_NotActiveSource_ToTv() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_TV); mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest"); mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); @@ -577,8 +576,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void handleOnStandby_ScreenOff_NotActiveSource_Broadcast() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest"); mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); @@ -597,8 +596,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void handleOnStandby_ScreenOff_NotActiveSource_None() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_NONE); mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest"); mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); @@ -617,8 +616,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void handleOnStandby_ScreenOff_ActiveSource_ToTv() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_TV); mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); @@ -637,8 +636,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void handleOnStandby_ScreenOff_ActiveSource_Broadcast() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); @@ -657,8 +656,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void handleOnStandby_ScreenOff_ActiveSource_None() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_NONE); mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); @@ -828,12 +827,12 @@ public class HdmiCecLocalDevicePlaybackTest { HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW); mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); mStandby = false; // 1. DUT is <AS>. - HdmiCecMessage message1 = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, - mPlaybackPhysicalAddress); + HdmiCecMessage message1 = HdmiCecMessageBuilder.buildActiveSource( + mHdmiCecLocalDevicePlayback.mAddress, mPlaybackPhysicalAddress); assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message1)).isTrue(); assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue(); assertThat(mStandby).isFalse(); @@ -1141,8 +1140,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void oneTouchPlay_SendStandbyOnSleepToTv() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_TV); mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() { @Override public void onComplete(int result) { @@ -1164,8 +1163,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void oneTouchPlay_SendStandbyOnSleepBroadcast() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() { @Override public void onComplete(int result) { @@ -1187,8 +1186,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void oneTouchPlay_SendStandbyOnSleepNone() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_NONE); mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() { @Override public void onComplete(int result) { @@ -1266,8 +1265,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void toggleAndFollowTvPower_ToTv_TvStatusOn() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_TV); mStandby = false; mHdmiControlService.toggleAndFollowTvPower(); HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV, @@ -1284,8 +1283,8 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void toggleAndFollowTvPower_Broadcast_TvStatusOn() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( - HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, - HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); mStandby = false; mHdmiControlService.toggleAndFollowTvPower(); HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV, diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 12414d99d991..d24b376793cd 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -88,17 +88,17 @@ public class HdmiCecLocalDeviceTvTest { } @Override - void writeStringSystemProperty(String key, String value) { + protected void writeStringSystemProperty(String key, String value) { // do nothing } @Override - PowerManager getPowerManager() { + protected PowerManager getPowerManager() { return powerManager; } @Override - HdmiCecConfig getHdmiCecConfig() { + protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java index a05cbb48a3f7..6285f58d07b3 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java @@ -56,6 +56,30 @@ public class HdmiCecMessageValidatorTest { } @Test + public void isValid_unregisteredSource() { + // Message invokes a broadcast response + // <Get Menu Language> + assertMessageValidity("F4:91").isEqualTo(OK); + // <Request Active Source> + assertMessageValidity("FF:85").isEqualTo(OK); + + // Message by CEC Switch + // <Routing Change> + assertMessageValidity("FF:80:00:00:10:00").isEqualTo(OK); + + // <Routing Information> + assertMessageValidity("FF:81:10:00").isEqualTo(OK); + + // Standby + assertMessageValidity("F4:36").isEqualTo(OK); + assertMessageValidity("FF:36").isEqualTo(OK); + + // <Report Physical Address> / <Active Source> + assertMessageValidity("FF:84:10:00:04").isEqualTo(OK); + assertMessageValidity("FF:82:10:00").isEqualTo(OK); + } + + @Test public void isValid_giveDevicePowerStatus() { assertMessageValidity("04:8F").isEqualTo(OK); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java index 3cc7c6b88a0d..670d51207e31 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java @@ -95,7 +95,7 @@ public class HdmiCecPowerStatusControllerTest { } @Override - void writeStringSystemProperty(String key, String value) { + protected void writeStringSystemProperty(String key, String value) { // do nothing } @@ -110,7 +110,7 @@ public class HdmiCecPowerStatusControllerTest { } @Override - HdmiCecConfig getHdmiCecConfig() { + protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 819bd01992cb..25138073ca40 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -151,7 +151,7 @@ public class HdmiControlServiceTest { } @Override - HdmiCecConfig getHdmiCecConfig() { + protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java index 0a225a06b380..f80b5737d27b 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java @@ -162,7 +162,7 @@ public class SystemAudioInitiationActionFromAvrTest { } @Override - HdmiCecConfig getHdmiCecConfig() { + protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } }; 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 f823bb957a33..2471210f6325 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 @@ -22,6 +22,8 @@ import static android.service.notification.NotificationListenerService.NOTIFICAT import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -58,6 +60,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.LauncherApps.ShortcutChangeCallback; +import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ShortcutInfo; @@ -136,6 +139,7 @@ public final class DataManagerTest { @Captor private ArgumentCaptor<ShortcutChangeCallback> mShortcutChangeCallbackCaptor; @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; + @Captor private ArgumentCaptor<Integer> mQueryFlagsCaptor; private ScheduledExecutorService mExecutorService; private NotificationChannel mNotificationChannel; @@ -854,6 +858,8 @@ public final class DataManagerTest { List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY); assertEquals(1, result.size()); assertEquals(shortcut.getId(), result.get(0).getShortcutInfo().getId()); + assertEquals(1, result.get(0).getShortcutInfo().getPersons().length); + assertEquals(CONTACT_URI, result.get(0).getShortcutInfo().getPersons()[0].getUri()); assertEquals(mParentNotificationChannel.getId(), result.get(0).getParentNotificationChannel().getId()); assertEquals(mStatusBarNotification.getPostTime(), result.get(0).getLastEventTimestamp()); @@ -861,6 +867,28 @@ public final class DataManagerTest { } @Test + public void testGetRecentConversationsGetsPersonsData() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS); + mDataManager.addOrUpdateConversationInfo(shortcut); + + NotificationListenerService listenerService = + mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); + listenerService.onNotificationPosted(mStatusBarNotification); + + List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY); + + verify(mShortcutServiceInternal).getShortcuts( + anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(), + mQueryFlagsCaptor.capture(), anyInt(), anyInt(), anyInt()); + Integer queryFlags = mQueryFlagsCaptor.getValue(); + assertThat(hasFlag(queryFlags, ShortcutQuery.FLAG_GET_PERSONS_DATA)).isTrue(); + } + + @Test public void testPruneOldRecentConversations() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); @@ -1068,6 +1096,13 @@ public final class DataManagerTest { return new UserInfo(userId, "", 0); } + /** + * Returns {@code true} iff {@link ShortcutQuery}'s {@code queryFlags} has {@code flag} set. + */ + private static boolean hasFlag(int queryFlags, int flag) { + return (queryFlags & flag) != 0; + } + private class TestContactsQueryHelper extends ContactsQueryHelper { private Uri mContactUri; diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java index f8e92ad71d8e..84551c51052c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java @@ -22,6 +22,7 @@ import android.util.ArraySet; import android.util.SparseArray; import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.parsing.pkg.PackageImpl; import java.io.File; import java.util.Map; @@ -38,14 +39,14 @@ public class PackageSettingBuilder { private int mPkgFlags; private int mPrivateFlags; private int mSharedUserId; - private String[] mUsesStaticLibraries; - private long[] mUsesStaticLibrariesVersions; - private Map<String, ArraySet<String>> mMimeGroups; private String mVolumeUuid; + private int mAppId; private SparseArray<PackageUserState> mUserStates = new SparseArray<>(); private AndroidPackage mPkg; - private int mAppId; private InstallSource mInstallSource; + private String[] mUsesStaticLibraries; + private long[] mUsesStaticLibrariesVersions; + private Map<String, ArraySet<String>> mMimeGroups; private PackageParser.SigningDetails mSigningDetails; public PackageSettingBuilder setPackage(AndroidPackage pkg) { @@ -143,6 +144,14 @@ public class PackageSettingBuilder { return this; } + public PackageSettingBuilder setInstallState(int userId, boolean installed) { + if (mUserStates.indexOfKey(userId) < 0) { + mUserStates.put(userId, new PackageUserState()); + } + mUserStates.get(userId).installed = installed; + return this; + } + public PackageSettingBuilder setInstallSource(InstallSource installSource) { mInstallSource = installSource; return this; @@ -173,6 +182,5 @@ public class PackageSettingBuilder { packageSetting.setUserState(mUserStates.keyAt(i), mUserStates.valueAt(i)); } return packageSetting; - } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index fcbb5ed1140c..d8c3979c9cf9 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -101,7 +101,8 @@ public class ScanTests { @Before public void setupDefaultAbiBehavior() throws Exception { when(mMockPackageAbiHelper.derivePackageAbi( - any(AndroidPackage.class), anyBoolean(), nullable(String.class))) + any(AndroidPackage.class), anyBoolean(), nullable(String.class), + any(File.class))) .thenReturn(new Pair<>( new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"), new PackageAbiHelper.NativeLibraryPaths( diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java index 3ce7a7d73d4a..eaf62cbb8201 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java @@ -227,8 +227,8 @@ public class DexMetadataHelperTest { File dm = createDexMetadataFile("install_split_base.apk"); try (FileInputStream is = new FileInputStream(base)) { ApkLite baseApk = PackageParser.parseApkLite(is.getFD(), base.getAbsolutePath(), 0); - PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null, - null, null); + PackageLite pkgLite = new PackageLite(null, baseApk.codePath, baseApk, null, null, null, + null, null, null); Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite)); } diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java index 92942bb91528..e816cbebd73f 100644 --- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java +++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java @@ -23,6 +23,7 @@ import static com.android.server.policy.DeviceStateProviderImpl.DEFAULT_DEVICE_S import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -170,16 +171,16 @@ public final class DeviceStateProviderImplTest { @Test public void create_sensor() throws Exception { - Sensor sensor = newSensor("sensor", Sensor.TYPE_HINGE_ANGLE); - when(mSensorManager.getSensorList(eq(sensor.getType()))).thenReturn(List.of(sensor)); + Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE); + when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor)); String configString = "<device-state-config>\n" + " <device-state>\n" + " <identifier>1</identifier>\n" + " <conditions>\n" + " <sensor>\n" + + " <type>" + sensor.getStringType() + "</type>\n" + " <name>" + sensor.getName() + "</name>\n" - + " <type>" + sensor.getType() + "</type>\n" + " <value>\n" + " <max>90</max>\n" + " </value>\n" @@ -190,8 +191,8 @@ public final class DeviceStateProviderImplTest { + " <identifier>2</identifier>\n" + " <conditions>\n" + " <sensor>\n" + + " <type>" + sensor.getStringType() + "</type>\n" + " <name>" + sensor.getName() + "</name>\n" - + " <type>" + sensor.getType() + "</type>\n" + " <value>\n" + " <min-inclusive>90</min-inclusive>\n" + " <max>180</max>\n" @@ -203,8 +204,8 @@ public final class DeviceStateProviderImplTest { + " <identifier>3</identifier>\n" + " <conditions>\n" + " <sensor>\n" + + " <type>" + sensor.getStringType() + "</type>\n" + " <name>" + sensor.getName() + "</name>\n" - + " <type>" + sensor.getType() + "</type>\n" + " <value>\n" + " <min-inclusive>180</min-inclusive>\n" + " </value>\n" @@ -262,13 +263,13 @@ public final class DeviceStateProviderImplTest { assertEquals(1, mIntegerCaptor.getValue().intValue()); } - private static Sensor newSensor(String name, int type) throws Exception { + private static Sensor newSensor(String name, String type) throws Exception { Constructor<Sensor> constructor = Sensor.class.getDeclaredConstructor(); constructor.setAccessible(true); Sensor sensor = constructor.newInstance(); FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mName"), name); - FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mType"), type); + FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mStringType"), type); return sensor; } diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java index fb6b29e4188e..9bd488d3df7e 100644 --- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java @@ -25,6 +25,7 @@ import android.content.Context; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerSaveState; +import android.provider.DeviceConfig; import android.provider.Settings.Global; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; @@ -47,19 +48,19 @@ public class BatterySaverPolicyTest extends AndroidTestCase { private static final int GPS_MODE = 0; // LOCATION_MODE_NO_CHANGE private static final int DEFAULT_GPS_MODE = PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF; - private static final String BATTERY_SAVER_CONSTANTS = "vibration_disabled=true," + private static final String BATTERY_SAVER_CONSTANTS = "disable_vibration=true," + "advertise_is_enabled=true," - + "animation_disabled=false," - + "soundtrigger_disabled=true," - + "firewall_disabled=false," - + "datasaver_disabled=false," - + "adjust_brightness_disabled=true," + + "disable_animation=false," + + "disable_soundtrigger=true," + + "enable_firewall=true," + + "enable_datasaver=true," + + "enable_brightness_adjustment=false," + "adjust_brightness_factor=0.7," - + "fullbackup_deferred=true," - + "keyvaluebackup_deferred=false," - + "gps_mode=0," // LOCATION_MODE_NO_CHANGE + + "defer_full_backup=true," + + "defer_keyvalue_backup=false," + + "location_mode=0," // LOCATION_MODE_NO_CHANGE + "enable_night_mode=false," - + "quick_doze_enabled=true"; + + "enable_quick_doze=true"; private static final String BATTERY_SAVER_INCORRECT_CONSTANTS = "vi*,!=,,true"; private class BatterySaverPolicyForTest extends BatterySaverPolicy { @@ -336,13 +337,15 @@ public class BatterySaverPolicyTest extends AndroidTestCase { } mBatterySaverPolicy.setAdaptivePolicyLocked( - Policy.fromSettings(BATTERY_SAVER_CONSTANTS, "")); + Policy.fromSettings(BATTERY_SAVER_CONSTANTS, "", + new DeviceConfig.Properties.Builder( + DeviceConfig.NAMESPACE_BATTERY_SAVER).build(), null)); verifyBatterySaverConstantsUpdated(); } public void testAutomotiveProjectionChanges_Full() { mBatterySaverPolicy.updateConstantsLocked( - "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF + "location_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF + ",enable_night_mode=true", ""); mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL); assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode) @@ -369,8 +372,10 @@ public class BatterySaverPolicyTest extends AndroidTestCase { public void testAutomotiveProjectionChanges_Adaptive() { mBatterySaverPolicy.setAdaptivePolicyLocked( Policy.fromSettings( - "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF - + ",enable_night_mode=true", "")); + "location_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF + + ",enable_night_mode=true", "", + new DeviceConfig.Properties.Builder( + DeviceConfig.NAMESPACE_BATTERY_SAVER).build(), null)); mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_ADAPTIVE); assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode) .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF); @@ -392,4 +397,188 @@ public class BatterySaverPolicyTest extends AndroidTestCase { assertTrue(mBatterySaverPolicy.getBatterySaverPolicy( ServiceType.NIGHT_MODE).batterySaverEnabled); } + + public void testUserSettingsOverrideDeviceConfig() { + Policy policy = Policy.fromSettings( + BatterySaverPolicy.KEY_ADJUST_BRIGHTNESS_FACTOR + "=.1" + + "," + BatterySaverPolicy.KEY_ADVERTISE_IS_ENABLED + "=true" + + "," + BatterySaverPolicy.KEY_DEFER_FULL_BACKUP + "=true" + + "," + BatterySaverPolicy.KEY_DEFER_KEYVALUE_BACKUP + "=true" + + "," + BatterySaverPolicy.KEY_DISABLE_ANIMATION + "=true" + + "," + BatterySaverPolicy.KEY_DISABLE_AOD + "=true" + + "," + BatterySaverPolicy.KEY_DISABLE_LAUNCH_BOOST + "=true" + + "," + BatterySaverPolicy.KEY_DISABLE_OPTIONAL_SENSORS + "=true" + + "," + BatterySaverPolicy.KEY_DISABLE_SOUNDTRIGGER + "=true" + + "," + BatterySaverPolicy.KEY_DISABLE_VIBRATION + "=true" + + "," + BatterySaverPolicy.KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + "=true" + + "," + BatterySaverPolicy.KEY_ENABLE_DATASAVER + "=true" + + "," + BatterySaverPolicy.KEY_ENABLE_FIREWALL + "=true" + + "," + BatterySaverPolicy.KEY_ENABLE_NIGHT_MODE + "=true" + + "," + BatterySaverPolicy.KEY_ENABLE_QUICK_DOZE + "=true" + + "," + BatterySaverPolicy.KEY_FORCE_ALL_APPS_STANDBY + "=true" + + "," + BatterySaverPolicy.KEY_FORCE_BACKGROUND_CHECK + "=true" + + "," + BatterySaverPolicy.KEY_LOCATION_MODE + + "=" + PowerManager.LOCATION_MODE_FOREGROUND_ONLY, + "", + new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_BATTERY_SAVER) + .setFloat(BatterySaverPolicy.KEY_ADJUST_BRIGHTNESS_FACTOR, .5f) + .setBoolean(BatterySaverPolicy.KEY_ADVERTISE_IS_ENABLED, false) + .setBoolean(BatterySaverPolicy.KEY_DEFER_FULL_BACKUP, false) + .setBoolean(BatterySaverPolicy.KEY_DEFER_KEYVALUE_BACKUP, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_ANIMATION, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_AOD, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_LAUNCH_BOOST, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_OPTIONAL_SENSORS, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_SOUNDTRIGGER, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_VIBRATION, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_BRIGHTNESS_ADJUSTMENT, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_DATASAVER, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_FIREWALL, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_NIGHT_MODE, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_QUICK_DOZE, false) + .setBoolean(BatterySaverPolicy.KEY_FORCE_ALL_APPS_STANDBY, false) + .setBoolean(BatterySaverPolicy.KEY_FORCE_BACKGROUND_CHECK, false) + .setInt(BatterySaverPolicy.KEY_LOCATION_MODE, + PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF) + .build(), + null); + assertEquals(.1f, policy.adjustBrightnessFactor); + assertTrue(policy.advertiseIsEnabled); + assertTrue(policy.deferFullBackup); + assertTrue(policy.deferKeyValueBackup); + assertTrue(policy.disableAnimation); + assertTrue(policy.disableAod); + assertTrue(policy.disableLaunchBoost); + assertTrue(policy.disableOptionalSensors); + assertTrue(policy.disableSoundTrigger); + assertTrue(policy.disableVibration); + assertTrue(policy.enableAdjustBrightness); + assertTrue(policy.enableDataSaver); + assertTrue(policy.enableFirewall); + assertTrue(policy.enableNightMode); + assertTrue(policy.enableQuickDoze); + assertTrue(policy.forceAllAppsStandby); + assertTrue(policy.forceBackgroundCheck); + assertEquals(PowerManager.LOCATION_MODE_FOREGROUND_ONLY, policy.locationMode); + } + + public void testDeviceConfigOverridesDefaults() { + Policy policy = Policy.fromSettings( + "", "", + new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_BATTERY_SAVER) + .setFloat(BatterySaverPolicy.KEY_ADJUST_BRIGHTNESS_FACTOR, .5f) + .setBoolean(BatterySaverPolicy.KEY_ADVERTISE_IS_ENABLED, false) + .setBoolean(BatterySaverPolicy.KEY_DEFER_FULL_BACKUP, false) + .setBoolean(BatterySaverPolicy.KEY_DEFER_KEYVALUE_BACKUP, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_ANIMATION, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_AOD, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_LAUNCH_BOOST, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_OPTIONAL_SENSORS, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_SOUNDTRIGGER, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_VIBRATION, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_BRIGHTNESS_ADJUSTMENT, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_DATASAVER, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_FIREWALL, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_NIGHT_MODE, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_QUICK_DOZE, false) + .setBoolean(BatterySaverPolicy.KEY_FORCE_ALL_APPS_STANDBY, false) + .setBoolean(BatterySaverPolicy.KEY_FORCE_BACKGROUND_CHECK, false) + .setInt(BatterySaverPolicy.KEY_LOCATION_MODE, + PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF) + .build(), + null); + assertEquals(.5f, policy.adjustBrightnessFactor); + assertFalse(policy.advertiseIsEnabled); + assertFalse(policy.deferFullBackup); + assertFalse(policy.deferKeyValueBackup); + assertFalse(policy.disableAnimation); + assertFalse(policy.disableAod); + assertFalse(policy.disableLaunchBoost); + assertFalse(policy.disableOptionalSensors); + assertFalse(policy.disableSoundTrigger); + assertFalse(policy.disableVibration); + assertFalse(policy.enableAdjustBrightness); + assertFalse(policy.enableDataSaver); + assertFalse(policy.enableFirewall); + assertFalse(policy.enableNightMode); + assertFalse(policy.enableQuickDoze); + assertFalse(policy.forceAllAppsStandby); + assertFalse(policy.forceBackgroundCheck); + assertEquals(PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF, + policy.locationMode); + } + + public void testDeviceConfig_AdaptiveValues() { + final String adaptiveSuffix = "_adaptive"; + Policy policy = Policy.fromSettings( + "", "", + new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_BATTERY_SAVER) + .setFloat(BatterySaverPolicy.KEY_ADJUST_BRIGHTNESS_FACTOR, .5f) + .setBoolean(BatterySaverPolicy.KEY_ADVERTISE_IS_ENABLED, false) + .setBoolean(BatterySaverPolicy.KEY_DEFER_FULL_BACKUP, false) + .setBoolean(BatterySaverPolicy.KEY_DEFER_KEYVALUE_BACKUP, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_ANIMATION, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_AOD, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_LAUNCH_BOOST, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_OPTIONAL_SENSORS, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_SOUNDTRIGGER, false) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_VIBRATION, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_BRIGHTNESS_ADJUSTMENT, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_DATASAVER, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_FIREWALL, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_NIGHT_MODE, false) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_QUICK_DOZE, false) + .setBoolean(BatterySaverPolicy.KEY_FORCE_ALL_APPS_STANDBY, false) + .setBoolean(BatterySaverPolicy.KEY_FORCE_BACKGROUND_CHECK, false) + .setInt(BatterySaverPolicy.KEY_LOCATION_MODE, + PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF) + .setFloat(BatterySaverPolicy.KEY_ADJUST_BRIGHTNESS_FACTOR + adaptiveSuffix, + .9f) + .setBoolean(BatterySaverPolicy.KEY_ADVERTISE_IS_ENABLED + adaptiveSuffix, + true) + .setBoolean(BatterySaverPolicy.KEY_DEFER_FULL_BACKUP + adaptiveSuffix, true) + .setBoolean(BatterySaverPolicy.KEY_DEFER_KEYVALUE_BACKUP + adaptiveSuffix, + true) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_ANIMATION + adaptiveSuffix, true) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_AOD + adaptiveSuffix, true) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_LAUNCH_BOOST + adaptiveSuffix, + true) + .setBoolean( + BatterySaverPolicy.KEY_DISABLE_OPTIONAL_SENSORS + adaptiveSuffix, + true) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_SOUNDTRIGGER + adaptiveSuffix, + true) + .setBoolean(BatterySaverPolicy.KEY_DISABLE_VIBRATION + adaptiveSuffix, true) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + + adaptiveSuffix, true) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_DATASAVER + adaptiveSuffix, true) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_FIREWALL + adaptiveSuffix, true) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_NIGHT_MODE + adaptiveSuffix, true) + .setBoolean(BatterySaverPolicy.KEY_ENABLE_QUICK_DOZE + adaptiveSuffix, true) + .setBoolean(BatterySaverPolicy.KEY_FORCE_ALL_APPS_STANDBY + adaptiveSuffix, + true) + .setBoolean(BatterySaverPolicy.KEY_FORCE_BACKGROUND_CHECK + adaptiveSuffix, + true) + .setInt(BatterySaverPolicy.KEY_LOCATION_MODE + adaptiveSuffix, + PowerManager.LOCATION_MODE_FOREGROUND_ONLY) + .build(), adaptiveSuffix); + assertEquals(.9f, policy.adjustBrightnessFactor); + assertTrue(policy.advertiseIsEnabled); + assertTrue(policy.deferFullBackup); + assertTrue(policy.deferKeyValueBackup); + assertTrue(policy.disableAnimation); + assertTrue(policy.disableAod); + assertTrue(policy.disableLaunchBoost); + assertTrue(policy.disableOptionalSensors); + assertTrue(policy.disableSoundTrigger); + assertTrue(policy.disableVibration); + assertTrue(policy.enableAdjustBrightness); + assertTrue(policy.enableDataSaver); + assertTrue(policy.enableFirewall); + assertTrue(policy.enableNightMode); + assertTrue(policy.enableQuickDoze); + assertTrue(policy.forceAllAppsStandby); + assertTrue(policy.forceBackgroundCheck); + assertEquals(PowerManager.LOCATION_MODE_FOREGROUND_ONLY, policy.locationMode); + } } diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java index 72d6caf1a5be..133f630b7a74 100644 --- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java +++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.metrics.LogMaker; +import android.util.IndentingPrintWriter; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -73,7 +74,7 @@ public class BatterySavingStatsTest { void assertDumpable() { final ByteArrayOutputStream out = new ByteArrayOutputStream(); - dump(new PrintWriter(out), ""); // Just make sure it won't crash. + dump(new IndentingPrintWriter(new PrintWriter(out))); // Just make sure it won't crash. } void advanceClock(int minutes) { diff --git a/services/tests/servicestests/src/com/android/server/tv/TvInputServiceManagerTest.java b/services/tests/servicestests/src/com/android/server/tv/TvInputServiceManagerTest.java new file mode 100644 index 000000000000..229e27b9a438 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/tv/TvInputServiceManagerTest.java @@ -0,0 +1,101 @@ +/* + * 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.tv; + +import static android.media.tv.TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING; +import static android.media.tv.TvInputManager.VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN; +import static android.media.tv.TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING; +import static android.media.tv.TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import com.android.internal.util.FrameworkStatsLog; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + + +/** + * Tests for {@link TvInputManagerService}. + */ +@Presubmit +@RunWith(JUnit4.class) +public class TvInputServiceManagerTest { + + @Test + public void getVideoUnavailableReasonForStatsd_tuning() { + assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd( + VIDEO_UNAVAILABLE_REASON_TUNING + )).isEqualTo( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_TUNING); + } + + @Test + public void getVideoUnavailableReasonForStatsd_unknown() { + assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd( + VIDEO_UNAVAILABLE_REASON_UNKNOWN + )).isEqualTo( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN); + } + + + @Test + public void getVideoUnavailableReasonForStatsd_casBuffering() { + assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd( + VIDEO_UNAVAILABLE_REASON_BUFFERING + )).isEqualTo( + FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_BUFFERING); + } + + @Test + public void getVideoUnavailableReasonForStatsd_casUnknown() { + assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd( + VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN + )).isEqualTo( + FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN); + } + + @Test + public void getVideoUnavailableReasonForStatsd_negative() { + assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd( + -1 + )).isEqualTo( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN); + } + + @Test + public void getVideoUnavailableReasonForStatsd_oneBelow() { + assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd( + VIDEO_UNAVAILABLE_REASON_UNKNOWN - 1 + )).isEqualTo( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN); + } + + @Test + public void getVideoUnavailableReasonForStatsd_oneAbove() { + assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd( + VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN + 1 + )).isEqualTo( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN); + } + +} diff --git a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt index 59cbd1ce1d7b..4c82818f71e4 100644 --- a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt +++ b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt @@ -17,6 +17,7 @@ package com.android.server.testutils import org.mockito.Answers +import org.mockito.ArgumentMatchers import org.mockito.Mockito import org.mockito.invocation.InvocationOnMock import org.mockito.stubbing.Answer @@ -53,7 +54,7 @@ inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.ja fun <T> spy(value: T, block: T.() -> Unit = {}) = Mockito.spy(value).apply(block) -fun <Type> Stubber.whenever(mock: Type) = Mockito.`when`(mock) +fun <Type> Stubber.whenever(mock: Type) = this.`when`(mock) fun <Type : Any?> whenever(mock: Type) = Mockito.`when`(mock) @Suppress("UNCHECKED_CAST") @@ -81,3 +82,5 @@ inline fun <reified T> spyThrowOnUnmocked(value: T?, block: T.() -> Unit = {}): inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit = {}) = spyThrowOnUnmocked<T>(null, block) + +inline fun <reified T : Any> nullable() = ArgumentMatchers.nullable(T::class.java)
\ No newline at end of file diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 00f706b13c58..e510b4fbfdd5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -153,7 +153,8 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { AccessibilityEvent.TYPES_ALL_MASK); when(mAccessibilityService.addClient(any(), anyInt())).thenReturn(serviceReturnValue); AccessibilityManager accessibilityManager = - new AccessibilityManager(Handler.getMain(), mAccessibilityService, 0); + new AccessibilityManager(getContext(), Handler.getMain(), mAccessibilityService, + 0, true); verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt()); assertTrue(accessibilityManager.isEnabled()); @@ -1191,7 +1192,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { NotificationRecord r = getLightsNotification(); mService.buzzBeepBlinkLocked(r); verifyNeverLights(); - assertFalse(r.isInterruptive()); + assertTrue(r.isInterruptive()); assertEquals(-1, r.getLastAudiblyAlertedMs()); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index fdc089ee2f7e..9b37e76e4c65 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -102,7 +102,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { String key = mKeys[i]; Ranking ranking = new Ranking(); service.getCurrentRanking().getRanking(key, ranking); - assertEquals(getVisibilityOverride(i), ranking.getVisibilityOverride()); + assertEquals(getVisibilityOverride(i), ranking.getLockscreenVisibilityOverride()); assertEquals(getOverrideGroupKey(key), ranking.getOverrideGroupKey()); assertEquals(!isIntercepted(i), ranking.matchesInterruptionFilter()); assertEquals(getSuppressedVisualEffects(i), ranking.getSuppressedVisualEffects()); @@ -173,7 +173,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { tweak.getKey(), tweak.getRank(), !tweak.matchesInterruptionFilter(), // note the inversion here! - tweak.getVisibilityOverride(), + tweak.getLockscreenVisibilityOverride(), tweak.getSuppressedVisualEffects(), tweak.getImportance(), tweak.getImportanceExplanation(), @@ -424,7 +424,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(comment, a.getKey(), b.getKey()); assertEquals(comment, a.getRank(), b.getRank()); assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter()); - assertEquals(comment, a.getVisibilityOverride(), b.getVisibilityOverride()); + assertEquals(comment, a.getLockscreenVisibilityOverride(), b.getLockscreenVisibilityOverride()); assertEquals(comment, a.getSuppressedVisualEffects(), b.getSuppressedVisualEffects()); assertEquals(comment, a.getImportance(), b.getImportance()); assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation()); @@ -440,7 +440,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(comment, a.getSmartReplies(), b.getSmartReplies()); assertEquals(comment, a.canBubble(), b.canBubble()); assertEquals(comment, a.isConversation(), b.isConversation()); - assertEquals(comment, a.getConversationShortcutInfo().getId(), b.getConversationShortcutInfo().getId()); + assertEquals(comment, a.getConversationShortcutInfo().getId(), + b.getConversationShortcutInfo().getId()); assertActionsEqual(a.getSmartActions(), b.getSmartActions()); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 5cf529a239dc..762ec93712cb 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -183,6 +183,7 @@ import com.android.server.notification.NotificationManagerService.NotificationAs import com.android.server.notification.NotificationManagerService.NotificationListeners; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -295,6 +296,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationHistoryManager mHistoryManager; @Mock StatsManager mStatsManager; + @Mock + MultiRateLimiter mToastRateLimiter; BroadcastReceiver mPackageIntentReceiver; NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( @@ -486,7 +489,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mGroupHelper, mAm, mAtm, mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mUm, mHistoryManager, mStatsManager, - mock(TelephonyManager.class), mAmi); + mock(TelephonyManager.class), mAmi, mToastRateLimiter); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); mService.setAudioManager(mAudioManager); @@ -566,7 +569,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { try { mService.onDestroy(); - } catch (IllegalStateException e) { + } catch (IllegalStateException | IllegalArgumentException e) { // can throw if a broadcast receiver was never registered } @@ -4888,6 +4891,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -4910,6 +4914,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -4928,6 +4933,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -4950,10 +4956,32 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testToastRateLimiterCanPreventsShowCallForCustomToast() throws Exception { + final String testPackage = "testPackageName"; + assertEquals(0, mService.mToastQueue.size()); + mService.isSystemUid = false; + setToastRateIsWithinQuota(false); // rate limit reached + + // package is not suspended + when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) + .thenReturn(false); + + setAppInForegroundForToasts(mUid, true); + + Binder token = new Binder(); + ITransientNotification callback = mock(ITransientNotification.class); + INotificationManager nmService = (INotificationManager) mService.mService; + + nmService.enqueueToast(testPackage, token, callback, 2000, 0); + verify(callback, times(0)).show(any()); + } + + @Test public void testAllowForegroundTextToasts() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -4972,6 +5000,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -4990,6 +5019,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5013,11 +5043,31 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testToastRateLimiterCanPreventsShowCallForTextToast() throws Exception { + final String testPackage = "testPackageName"; + assertEquals(0, mService.mToastQueue.size()); + mService.isSystemUid = false; + setToastRateIsWithinQuota(false); // rate limit reached + + // package is not suspended + when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) + .thenReturn(false); + + Binder token = new Binder(); + INotificationManager nmService = (INotificationManager) mService.mService; + + nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null); + verify(mStatusBar, times(0)) + .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any()); + } + + @Test public void backgroundSystemCustomToast_callsSetProcessImportantAsForegroundForToast() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = true; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5042,6 +5092,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5062,6 +5113,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5081,6 +5133,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5097,6 +5150,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5117,6 +5171,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5139,6 +5194,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = true; + setToastRateIsWithinQuota(true); // package is suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5161,6 +5217,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5188,6 +5245,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAtm.hasResumedActivity(uid)).thenReturn(inForeground); } + private void setToastRateIsWithinQuota(boolean isWithinQuota) { + when(mToastRateLimiter.isWithinQuota( + anyInt(), + anyString(), + eq(NotificationManagerService.TOAST_QUOTA_TAG))) + .thenReturn(isWithinQuota); + } + @Test public void testOnPanelRevealedAndHidden() { int items = 5; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 98c4a2da6a4f..4d2a4784b5d9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -97,6 +97,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.testing.TestableContentResolver; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.Pair; import android.util.StatsEvent; import android.util.TypedXmlPullParser; @@ -3320,7 +3321,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel2.setImportantConversation(true); mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false); - List<ConversationChannelWrapper> convos = mHelper.getConversations(false); + List<ConversationChannelWrapper> convos = + mHelper.getConversations(IntArray.wrap(new int[] {0}), false); assertEquals(3, convos.size()); assertTrue(conversationWrapperContainsChannel(convos, channel)); @@ -3329,6 +3331,44 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testGetConversations_multiUser() { + String convoId = "convo"; + NotificationChannel messages = + new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT); + mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false); + + NotificationChannel messagesUser10 = + new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT); + mHelper.createNotificationChannel( + PKG_O, UID_O + UserHandle.PER_USER_RANGE, messagesUser10, true, false); + + NotificationChannel messagesFromB = + new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT); + messagesFromB.setConversationId(messages.getId(), "different convo"); + mHelper.createNotificationChannel(PKG_O, UID_O, messagesFromB, true, false); + + NotificationChannel messagesFromBUser10 = + new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT); + messagesFromBUser10.setConversationId(messagesUser10.getId(), "different convo"); + mHelper.createNotificationChannel( + PKG_O, UID_O + UserHandle.PER_USER_RANGE, messagesFromBUser10, true, false); + + + List<ConversationChannelWrapper> convos = + mHelper.getConversations(IntArray.wrap(new int[] {0}), false); + + assertEquals(1, convos.size()); + assertTrue(conversationWrapperContainsChannel(convos, messagesFromB)); + + convos = + mHelper.getConversations(IntArray.wrap(new int[] {0, UserHandle.getUserId(UID_O + UserHandle.PER_USER_RANGE)}), false); + + assertEquals(2, convos.size()); + assertTrue(conversationWrapperContainsChannel(convos, messagesFromB)); + assertTrue(conversationWrapperContainsChannel(convos, messagesFromBUser10)); + } + + @Test public void testGetConversations_notDemoted() { String convoId = "convo"; NotificationChannel messages = @@ -3358,7 +3398,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel2.setImportantConversation(true); mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false); - List<ConversationChannelWrapper> convos = mHelper.getConversations(false); + List<ConversationChannelWrapper> convos = + mHelper.getConversations(IntArray.wrap(new int[] {0}), false); assertEquals(2, convos.size()); assertTrue(conversationWrapperContainsChannel(convos, channel)); @@ -3396,7 +3437,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel2.setConversationId(calls.getId(), convoId); mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false); - List<ConversationChannelWrapper> convos = mHelper.getConversations(true); + List<ConversationChannelWrapper> convos = + mHelper.getConversations(IntArray.wrap(new int[] {0}), true); assertEquals(2, convos.size()); assertTrue(conversationWrapperContainsChannel(convos, channel)); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index a80f62ab09ee..4ce237e3aadc 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -67,6 +67,7 @@ import com.android.server.lights.LightsManager; import com.android.server.notification.NotificationManagerService.NotificationAssistants; import com.android.server.notification.NotificationManagerService.NotificationListeners; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -156,7 +157,8 @@ public class RoleObserverTest extends UiServiceTestCase { mock(UriGrantsManagerInternal.class), mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class), mock(StatsManager.class), mock(TelephonyManager.class), - mock(ActivityManagerInternal.class)); + mock(ActivityManagerInternal.class), + mock(MultiRateLimiter.class)); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { throw e; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java index e5ae2d3f63ab..f43e5a83c95d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java @@ -27,8 +27,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; +import android.app.Person; import android.content.pm.LauncherApps; +import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutQueryWrapper; import android.content.pm.ShortcutServiceInternal; import android.os.UserHandle; import android.service.notification.StatusBarNotification; @@ -44,6 +47,7 @@ import org.junit.Ignore; 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.MockitoAnnotations; @@ -60,6 +64,7 @@ public class ShortcutHelperTest extends UiServiceTestCase { private static final String SHORTCUT_ID = "shortcut"; private static final String PKG = "pkg"; private static final String KEY = "key"; + private static final Person PERSON = mock(Person.class); @Mock LauncherApps mLauncherApps; @@ -78,6 +83,8 @@ public class ShortcutHelperTest extends UiServiceTestCase { @Mock ShortcutInfo mShortcutInfo; + @Captor private ArgumentCaptor<ShortcutQuery> mShortcutQueryCaptor; + ShortcutHelper mShortcutHelper; @Before @@ -298,6 +305,7 @@ public class ShortcutHelperTest extends UiServiceTestCase { when(si.getUserId()).thenReturn(UserHandle.USER_SYSTEM); when(si.isLongLived()).thenReturn(true); when(si.isEnabled()).thenReturn(true); + when(si.getPersons()).thenReturn(new Person[]{PERSON}); ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); shortcuts.add(si); when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts); @@ -308,4 +316,23 @@ public class ShortcutHelperTest extends UiServiceTestCase { assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)) .isSameInstanceAs(si); } + + @Test + public void testGetValidShortcutInfo_hasGetPersonsDataFlag() { + + ShortcutInfo info = mShortcutHelper.getValidShortcutInfo( + "a", "p", UserHandle.SYSTEM); + verify(mLauncherApps).getShortcuts(mShortcutQueryCaptor.capture(), any()); + ShortcutQueryWrapper shortcutQuery = + new ShortcutQueryWrapper(mShortcutQueryCaptor.getValue()); + assertThat(hasFlag(shortcutQuery.getQueryFlags(), ShortcutQuery.FLAG_GET_PERSONS_DATA)) + .isTrue(); + } + + /** + * Returns {@code true} iff {@link ShortcutQuery}'s {@code queryFlags} has {@code flag} set. + */ + private static boolean hasFlag(int queryFlags, int flag) { + return (queryFlags & flag) != 0; + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java new file mode 100644 index 000000000000..3998129659c6 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java @@ -0,0 +1,246 @@ +/* + * 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.notification; + +import static android.app.Notification.VISIBILITY_PRIVATE; +import static android.app.Notification.VISIBILITY_SECRET; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; +import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE; +import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS; +import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.Notification; +import android.app.Notification.Builder; +import android.app.NotificationChannel; +import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; +import android.content.Intent; +import android.graphics.drawable.Icon; +import android.media.session.MediaSession; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.UiServiceTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class VisibilityExtractorTest extends UiServiceTestCase { + + @Mock RankingConfig mConfig; + @Mock + DevicePolicyManager mDpm; + + private String mPkg = "com.android.server.notification"; + private int mId = 1001; + private String mTag = null; + private int mUid = 1000; + private int mPid = 2000; + private int mUser = ActivityManager.getCurrentUser(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext.addMockSystemService(DevicePolicyManager.class, mDpm); + } + + private NotificationRecord getNotificationRecord(int visibility) { + NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT); + channel.setLockscreenVisibility(visibility); + when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel); + + final Builder builder = new Builder(getContext()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon); + + Notification n = builder.build(); + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid, + mPid, n, UserHandle.of(mUser), null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + return r; + } + + // + // Tests + // + + @Test + public void testGlobalAllDpmAllChannelAll() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0); + + NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE); + + extractor.process(r); + + assertEquals(VISIBILITY_NO_OVERRIDE, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalNoneDpmAllChannelAll() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(false); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0); + + NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE); + + extractor.process(r); + + assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalSomeDpmAllChannelAll() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(false); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0); + + NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE); + + extractor.process(r); + + assertEquals(VISIBILITY_PRIVATE, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalAllDpmNoneChannelAll() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn( + KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); + + NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE); + + extractor.process(r); + + assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalAllDpmSomeChannelAll() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn( + KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + + NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE); + + extractor.process(r); + + assertEquals(VISIBILITY_PRIVATE, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalAllDpmAllChannelNone() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0); + + NotificationRecord r = getNotificationRecord(VISIBILITY_SECRET); + + extractor.process(r); + + assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalAllDpmAllChannelSome() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0); + + NotificationRecord r = getNotificationRecord(VISIBILITY_PRIVATE); + + extractor.process(r); + + assertEquals(VISIBILITY_PRIVATE, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalAllDpmSomeChannelNone() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn( + KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + + NotificationRecord r = getNotificationRecord(VISIBILITY_SECRET); + + extractor.process(r); + + assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride()); + } + +} diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java index a4436951f48b..dd0c162d8467 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java @@ -75,7 +75,6 @@ public class SliceManagerServiceTest extends UiServiceTestCase { LocalServices.addService(UsageStatsManagerInternal.class, mock(UsageStatsManagerInternal.class)); mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); - mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED); mContextSpy = spy(mContext); mService = spy(new SliceManagerService(mContextSpy, TestableLooper.get(this).getLooper())); @@ -90,6 +89,7 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test public void testAddPinCreatesPinned() throws RemoteException { + grantSlicePermission(); doReturn("pkg").when(mService).getDefaultHome(anyInt()); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); @@ -99,6 +99,7 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test public void testRemovePinDestroysPinned() throws RemoteException { + grantSlicePermission(); doReturn("pkg").when(mService).getDefaultHome(anyInt()); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); @@ -130,11 +131,13 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test(expected = IllegalStateException.class) public void testNoPinThrow() throws Exception { + grantSlicePermission(); mService.getPinnedSpecs(TEST_URI, "pkg"); } @Test public void testGetPinnedSpecs() throws Exception { + grantSlicePermission(); SliceSpec[] specs = new SliceSpec[] { new SliceSpec("Something", 1) }; mService.pinSlice("pkg", TEST_URI, specs, mToken); @@ -143,4 +146,10 @@ public class SliceManagerServiceTest extends UiServiceTestCase { assertEquals(specs, mService.getPinnedSpecs(TEST_URI, "pkg")); } + private void grantSlicePermission() { + doReturn(PERMISSION_GRANTED).when(mService).checkSlicePermission( + eq(TEST_URI), anyString(), anyString(), anyInt(), anyInt(), any()); + doReturn(PERMISSION_GRANTED).when(mService).checkAccess( + anyString(), eq(TEST_URI), anyInt(), anyInt()); + } } diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java new file mode 100644 index 000000000000..75479de26b1d --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java @@ -0,0 +1,208 @@ +/* + * 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.policy; + +import static android.view.KeyEvent.ACTION_DOWN; +import static android.view.KeyEvent.ACTION_UP; +import static android.view.KeyEvent.KEYCODE_BACK; +import static android.view.KeyEvent.KEYCODE_POWER; +import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN; +import static android.view.KeyEvent.KEYCODE_VOLUME_UP; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import android.os.Handler; +import android.os.Looper; +import android.os.SystemClock; +import android.view.KeyEvent; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link KeyCombinationManager}. + * + * Build/Install/Run: + * atest KeyCombinationTests + */ + +@SmallTest +public class KeyCombinationTests { + private KeyCombinationManager mKeyCombinationManager; + + private boolean mAction1Triggered = false; + private boolean mAction2Triggered = false; + private boolean mAction3Triggered = false; + + private boolean mPreCondition = true; + private static final long SCHEDULE_TIME = 300; + + @Before + public void setUp() { + mKeyCombinationManager = new KeyCombinationManager(); + initKeyCombinationRules(); + } + + private void initKeyCombinationRules() { + // Rule 1 : power + volume_down trigger action immediately. + mKeyCombinationManager.addRule( + new KeyCombinationManager.TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, + KEYCODE_POWER) { + @Override + void execute() { + mAction1Triggered = true; + } + + @Override + void cancel() { + } + }); + + // Rule 2 : volume_up + volume_down with condition. + mKeyCombinationManager.addRule( + new KeyCombinationManager.TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, + KEYCODE_VOLUME_UP) { + @Override + boolean preCondition() { + return mPreCondition; + } + + @Override + void execute() { + mAction2Triggered = true; + } + + @Override + void cancel() { + } + }); + + // Rule 3 : power + volume_up schedule and trigger action after timeout. + mKeyCombinationManager.addRule( + new KeyCombinationManager.TwoKeysCombinationRule(KEYCODE_VOLUME_UP, KEYCODE_POWER) { + final Runnable mAction = new Runnable() { + @Override + public void run() { + mAction3Triggered = true; + } + }; + final Handler mHandler = new Handler(Looper.getMainLooper()); + + @Override + void execute() { + mHandler.postDelayed(mAction, SCHEDULE_TIME); + } + + @Override + void cancel() { + mHandler.removeCallbacks(mAction); + } + }); + } + + private void pressKeys(long firstKeyTime, int firstKeyCode, long secondKeyTime, + int secondKeyCode) { + pressKeys(firstKeyTime, firstKeyCode, secondKeyTime, secondKeyCode, 0); + } + + private void pressKeys(long firstKeyTime, int firstKeyCode, long secondKeyTime, + int secondKeyCode, long pressTime) { + final KeyEvent firstKeyDown = new KeyEvent(firstKeyTime, firstKeyTime, ACTION_DOWN, + firstKeyCode, 0 /* repeat */, 0 /* metaState */); + final KeyEvent secondKeyDown = new KeyEvent(secondKeyTime, secondKeyTime, ACTION_DOWN, + secondKeyCode, 0 /* repeat */, 0 /* metaState */); + + mKeyCombinationManager.interceptKey(firstKeyDown, true); + mKeyCombinationManager.interceptKey(secondKeyDown, true); + + // keep press down. + try { + Thread.sleep(pressTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + final KeyEvent firstKeyUp = new KeyEvent(firstKeyTime, firstKeyTime, ACTION_UP, + firstKeyCode, 0 /* repeat */, 0 /* metaState */); + final KeyEvent secondKeyUp = new KeyEvent(secondKeyTime, secondKeyTime, ACTION_UP, + secondKeyCode, 0 /* repeat */, 0 /* metaState */); + + mKeyCombinationManager.interceptKey(firstKeyUp, true); + mKeyCombinationManager.interceptKey(secondKeyUp, true); + } + + @Test + public void testTriggerRule() { + final long eventTime = SystemClock.uptimeMillis(); + pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN); + assertTrue(mAction1Triggered); + + pressKeys(eventTime, KEYCODE_VOLUME_UP, eventTime, KEYCODE_VOLUME_DOWN); + assertTrue(mAction2Triggered); + + pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_UP, SCHEDULE_TIME + 50); + assertTrue(mAction3Triggered); + } + + /** + * Nothing should happen if there is no definition. + */ + @Test + public void testNotTrigger_NoRule() { + final long eventTime = SystemClock.uptimeMillis(); + pressKeys(eventTime, KEYCODE_BACK, eventTime, KEYCODE_VOLUME_DOWN); + assertFalse(mAction1Triggered); + assertFalse(mAction2Triggered); + assertFalse(mAction3Triggered); + } + + /** + * Nothing should happen if the interval of press time is too long. + */ + @Test + public void testNotTrigger_Interval() { + final long eventTime = SystemClock.uptimeMillis(); + final long earlyEventTime = eventTime - 200; // COMBINE_KEY_DELAY_MILLIS = 150; + pressKeys(earlyEventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN); + assertFalse(mAction1Triggered); + } + + /** + * Nothing should happen if the condition is false. + */ + @Test + public void testNotTrigger_Condition() { + final long eventTime = SystemClock.uptimeMillis(); + // we won't trigger action 2 because the condition is false. + mPreCondition = false; + pressKeys(eventTime, KEYCODE_VOLUME_UP, eventTime, KEYCODE_VOLUME_DOWN); + assertFalse(mAction2Triggered); + } + + /** + * Nothing should happen if the keys released too early. + */ + @Test + public void testNotTrigger_EarlyRelease() { + final long eventTime = SystemClock.uptimeMillis(); + pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_UP); + assertFalse(mAction3Triggered); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 3d31824e5aae..080f04efa9b4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -80,13 +80,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { public void testActivityFinish() { final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); - assertTrue("Activity must be finished", mAtm.finishActivity(activity.appToken, - 0 /* resultCode */, null /* resultData */, + assertTrue("Activity must be finished", mAtm.mActivityClientController.finishActivity( + activity.appToken, 0 /* resultCode */, null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY)); assertTrue(activity.finishing); assertTrue("Duplicate activity finish request must also return 'true'", - mAtm.finishActivity(activity.appToken, 0 /* resultCode */, + mAtm.mActivityClientController.finishActivity(activity.appToken, 0 /* resultCode */, null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY)); } @@ -225,7 +225,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { //to simulate NPE doReturn(null).when(record).getParent(); - mAtm.enterPictureInPictureMode(token, params); + mAtm.mActivityClientController.enterPictureInPictureMode(token, params); //if record's null parent is not handled gracefully, test will fail with NPE mockSession.finishMocking(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java index bc91c709aeb1..f536cd0b0ed4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java @@ -61,7 +61,7 @@ public class DisplayAreaGroupTest extends WindowTestsBase { mTaskDisplayArea = new TaskDisplayArea( mDisplayContent, mWm, "TDA1", FEATURE_VENDOR_FIRST + 1); mDisplayAreaGroup.addChild(mTaskDisplayArea, POSITION_TOP); - mDisplayContent.setLastFocusedTaskDisplayArea(mTaskDisplayArea); + mDisplayContent.onLastFocusedTaskDisplayAreaChanged(mTaskDisplayArea); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index 8e6b6fab19eb..e47913fba7ab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -20,6 +20,7 @@ import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; @@ -31,6 +32,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST; import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; @@ -45,6 +47,7 @@ import static org.testng.Assert.assertThrows; import static java.util.stream.Collectors.toList; import android.content.res.Resources; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.platform.test.annotations.Presubmit; @@ -82,7 +85,7 @@ public class DisplayAreaPolicyBuilderTest { private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null); private WindowManagerService mWms; private RootDisplayArea mRoot; - private DisplayArea<WindowContainer> mImeContainer; + private DisplayArea.Tokens mImeContainer; private DisplayContent mDisplayContent; private TaskDisplayArea mDefaultTaskDisplayArea; private List<TaskDisplayArea> mTaskDisplayAreaList; @@ -95,7 +98,7 @@ public class DisplayAreaPolicyBuilderTest { public void setup() { mWms = mSystemServices.getWindowManagerService(); mRoot = new SurfacelessDisplayAreaRoot(mWms); - mImeContainer = new DisplayArea<>(mWms, ABOVE_TASKS, "Ime"); + mImeContainer = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "ImeContainer"); mDisplayContent = mock(DisplayContent.class); mDefaultTaskDisplayArea = new TaskDisplayArea(mDisplayContent, mWms, "Tasks", FEATURE_DEFAULT_TASK_CONTAINER); @@ -113,11 +116,13 @@ public class DisplayAreaPolicyBuilderTest { final Feature bar; DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy = new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) - .addFeature(foo = new Feature.Builder(mPolicy, "Foo", 0) + .addFeature(foo = new Feature.Builder(mPolicy, "Foo", + FEATURE_VENDOR_FIRST) .upTo(TYPE_STATUS_BAR) .and(TYPE_NAVIGATION_BAR) .build()) - .addFeature(bar = new Feature.Builder(mPolicy, "Bar", 1) + .addFeature(bar = new Feature.Builder(mPolicy, "Bar", + FEATURE_VENDOR_FIRST + 1) .all() .except(TYPE_STATUS_BAR) .build()) @@ -148,6 +153,10 @@ public class DisplayAreaPolicyBuilderTest { // The IME is below both foo and bar. assertThat(fooDescendantMatcher.matches(mImeContainer)).isTrue(); assertThat(barDescendantMatcher.matches(mImeContainer)).isTrue(); + assertThat(policy.findAreaForToken(tokenOfType(TYPE_INPUT_METHOD))) + .isEqualTo(mImeContainer); + assertThat(policy.findAreaForToken(tokenOfType(TYPE_INPUT_METHOD_DIALOG))) + .isEqualTo(mImeContainer); List<DisplayArea<?>> actualOrder = collectLeafAreas(mRoot); Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, mImeContainer, @@ -235,12 +244,14 @@ public class DisplayAreaPolicyBuilderTest { final Feature other; DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy = new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) - .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable", 0) + .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable", + FEATURE_VENDOR_FIRST) .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new) .build()) - .addFeature(other = new Feature.Builder(mPolicy, "Other", 1) + .addFeature(other = new Feature.Builder(mPolicy, "Other", + FEATURE_VENDOR_FIRST + 1) .all() .build()) .setImeContainer(mImeContainer) @@ -308,9 +319,7 @@ public class DisplayAreaPolicyBuilderTest { builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( mGroupRoot1) .setImeContainer(mImeContainer) - .setTaskDisplayAreas(Lists.newArrayList( - new TaskDisplayArea(mDisplayContent, mWms, "testTda", - FEATURE_VENDOR_FIRST + 1)))); + .setTaskDisplayAreas(Lists.newArrayList(mTda1))); assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); @@ -335,11 +344,144 @@ public class DisplayAreaPolicyBuilderTest { .setTaskDisplayAreas(mTaskDisplayAreaList)); builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( mGroupRoot2) + .setTaskDisplayAreas(Lists.newArrayList(mTda1))); + + builder4.build(mWms); + } + + @Test + public void testBuilder_rootHasUniqueId() { + // Root must have different id from all roots. + final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder(); + builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + final RootDisplayArea groupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1", + mRoot.mFeatureId); + builder1.addDisplayAreaGroupHierarchy( + new DisplayAreaPolicyBuilder.HierarchyBuilder(groupRoot1) + .setTaskDisplayAreas(Lists.newArrayList(mTda1))); + + assertThrows(IllegalStateException.class, () -> builder1.build(mWms)); + + // Root must have different id from all TDAs. + final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); + builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) .setTaskDisplayAreas(Lists.newArrayList( + mDefaultTaskDisplayArea, new TaskDisplayArea(mDisplayContent, mWms, "testTda", - FEATURE_VENDOR_FIRST + 1)))); + mRoot.mFeatureId)))); - builder4.build(mWms); + assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); + + // Root must have different id from all features. + final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder(); + builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList) + .addFeature(new Feature.Builder(mPolicy, "testFeature", mRoot.mFeatureId) + .all() + .build())); + + assertThrows(IllegalStateException.class, () -> builder3.build(mWms)); + } + + @Test + public void testBuilder_taskDisplayAreaHasUniqueId() { + // TDA must have different id from all TDAs. + final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder(); + final List<TaskDisplayArea> tdaList = Lists.newArrayList( + mDefaultTaskDisplayArea, + mTda1, + new TaskDisplayArea(mDisplayContent, mWms, "tda2", mTda1.mFeatureId)); + builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(tdaList)); + + assertThrows(IllegalStateException.class, () -> builder.build(mWms)); + + // TDA must have different id from all features. + final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); + builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList( + mDefaultTaskDisplayArea, + mTda1)) + .addFeature(new Feature.Builder(mPolicy, "testFeature", mTda1.mFeatureId) + .all() + .build())); + + assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); + } + + @Test + public void testBuilder_featureHasUniqueId() { + // Feature must have different id from features below the same root. + final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder(); + builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList) + .addFeature(new Feature.Builder(mPolicy, "feature1", FEATURE_VENDOR_FIRST + 10) + .all() + .build()) + .addFeature(new Feature.Builder(mPolicy, "feature2", FEATURE_VENDOR_FIRST + 10) + .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) + .build())); + + assertThrows(IllegalStateException.class, () -> builder.build(mWms)); + + // Features below different root can have the same id. + final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); + builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList) + .addFeature(new Feature.Builder(mPolicy, "feature1", FEATURE_VENDOR_FIRST + 10) + .all() + .build())); + builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setTaskDisplayAreas(Lists.newArrayList(mTda1)) + .addFeature(new Feature.Builder(mPolicy, "feature2", FEATURE_VENDOR_FIRST + 10) + .all() + .build())); + + builder2.build(mWms); + } + + @Test + public void testBuilder_idsNotGreaterThanFeatureVendorLast() { + // Root id should not be greater than FEATURE_VENDOR_LAST. + final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder(); + final RootDisplayArea root = new SurfacelessDisplayAreaRoot(mWms, "testRoot", + FEATURE_VENDOR_LAST + 1); + builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + + assertThrows(IllegalStateException.class, () -> builder1.build(mWms)); + + // TDA id should not be greater than FEATURE_VENDOR_LAST. + final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); + builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList( + mDefaultTaskDisplayArea, + new TaskDisplayArea(mDisplayContent, mWms, "testTda", + FEATURE_VENDOR_LAST + 1)))); + + assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); + + // Feature id should not be greater than FEATURE_VENDOR_LAST. + final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder(); + builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList) + .addFeature(new Feature.Builder(mPolicy, "testFeature", FEATURE_VENDOR_LAST + 1) + .all() + .build())); + + assertThrows(IllegalStateException.class, () -> builder3.build(mWms)); } @Test @@ -444,8 +586,8 @@ public class DisplayAreaPolicyBuilderTest { .setTaskDisplayAreas(mTaskDisplayAreaList)) .addDisplayAreaGroupHierarchy(hierarchy1) .addDisplayAreaGroupHierarchy(hierarchy2) - .setSelectRootForWindowFunc((token, options) -> { - if (token.windowType == TYPE_STATUS_BAR) { + .setSelectRootForWindowFunc((type, options) -> { + if (type == TYPE_STATUS_BAR) { return mGroupRoot1; } return mGroupRoot2; @@ -547,7 +689,7 @@ public class DisplayAreaPolicyBuilderTest { private Map<DisplayArea<?>, Set<Integer>> calculateZSets( DisplayAreaPolicyBuilder.Result policy, - DisplayArea<WindowContainer> ime, + DisplayArea.Tokens ime, DisplayArea<Task> tasks) { Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>(); int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION, @@ -599,10 +741,9 @@ public class DisplayAreaPolicyBuilderTest { } private WindowToken tokenOfType(int type) { - WindowToken m = mock(WindowToken.class); - when(m.getWindowLayerFromType()).thenReturn( - mPolicy.getWindowLayerFromTypeLw(type, false /* canAddInternalSystemWindow */)); - return m; + WindowToken token = new WindowToken(mWms, new Binder(), type, false /* persistOnEmpty */, + mDisplayContent, false /* ownerCanManageAppTokens */); + return token; } private static void assertMatchLayerOrder(List<DisplayArea<?>> actualOrder, diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java index 5a47493c12cd..496b2b744712 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java @@ -69,7 +69,7 @@ public class DisplayAreaPolicyTests { WindowManagerService wms = mSystemServices.getWindowManagerService(); mRoot = new SurfacelessDisplayAreaRoot(wms); spyOn(mRoot); - DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime"); + DisplayArea.Tokens ime = new DisplayArea.Tokens(wms, ABOVE_TASKS, "Ime"); DisplayContent displayContent = mock(DisplayContent.class); doReturn(true).when(displayContent).isTrusted(); mTaskDisplayArea1 = new TaskDisplayArea(displayContent, wms, "Tasks1", @@ -143,7 +143,7 @@ public class DisplayAreaPolicyTests { FEATURE_VENDOR_FIRST + 5); final TaskDisplayArea taskDisplayArea5 = new TaskDisplayArea(displayContent, wms, "Tasks5", FEATURE_VENDOR_FIRST + 6); - final DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime"); + final DisplayArea.Tokens ime = new DisplayArea.Tokens(wms, ABOVE_TASKS, "Ime"); final DisplayAreaPolicy policy = new DisplayAreaPolicyBuilder() .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root) .setImeContainer(ime) diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java index 351426ae78b2..2d289808dd26 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java @@ -81,7 +81,7 @@ public class DisplayAreaProviderTest { @Override public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, - RootDisplayArea root, DisplayArea<? extends WindowContainer> imeContainer) { + RootDisplayArea root, DisplayArea.Tokens imeContainer) { throw new RuntimeException("test stub"); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index 025c5a6bb180..6f5a874114ea 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -458,8 +458,7 @@ public class DisplayAreaTest extends WindowTestsBase { @Test public void testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged() { - final TaskDisplayArea tda = - mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); final Task stack = new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); final ActivityRecord activity = stack.getTopNonFinishingActivity(); @@ -478,6 +477,27 @@ public class DisplayAreaTest extends WindowTestsBase { verify(mDisplayContent).onDescendantOrientationChanged(any()); } + @Test + public void testSetIgnoreOrientationRequest_updateOrientationRequestingTaskDisplayArea() { + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); + final Task stack = + new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); + final ActivityRecord activity = stack.getTopNonFinishingActivity(); + + mDisplayContent.setFocusedApp(activity); + assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda); + + // TDA is no longer handling orientation request, clear the last focused TDA. + tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isNull(); + + // TDA now handles orientation request, update last focused TDA based on the focused app. + tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + + assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda); + } + private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> { private TestDisplayArea(WindowManagerService wms, Rect bounds) { super(wms, ANY, "half display area"); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java index 554160ccbb82..7c4f7db48022 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java @@ -16,7 +16,9 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -28,7 +30,6 @@ import static org.junit.Assert.assertTrue; import android.annotation.Nullable; import android.platform.test.annotations.Presubmit; import android.util.TypedXmlPullParser; -import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.Display; import android.view.DisplayAddress; @@ -69,7 +70,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { private static final File TEST_FOLDER = getInstrumentation().getTargetContext().getCacheDir(); - private TestStorage mBaseSettingsStorage; + private TestStorage mDefaultVendorSettingsStorage; + private TestStorage mSecondaryVendorSettingsStorage; private TestStorage mOverrideSettingsStorage; private DisplayContent mPrimaryDisplay; @@ -79,7 +81,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { public void setUp() throws Exception { deleteRecursively(TEST_FOLDER); - mBaseSettingsStorage = new TestStorage(); + mDefaultVendorSettingsStorage = new TestStorage(); + mSecondaryVendorSettingsStorage = new TestStorage(); mOverrideSettingsStorage = new TestStorage(); mPrimaryDisplay = mWm.getDefaultDisplayContentLocked(); @@ -122,7 +125,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { // Update settings with new value, should trigger write to injector. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( - mBaseSettingsStorage, mOverrideSettingsStorage); + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(mPrimaryDisplayInfo); overrideSettings.mForcedDensity = 200; provider.updateOverrideSettings(mPrimaryDisplayInfo, overrideSettings); @@ -162,12 +165,55 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { } @Test + public void testReadingDisplaySettingsFromStorage_secondayVendorDisplaySettingsLocation() { + final String displayIdentifier = mSecondaryDisplay.getDisplayInfo().uniqueId; + prepareSecondaryDisplaySettings(displayIdentifier); + + final DisplayWindowSettingsProvider provider = + new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage, + mOverrideSettingsStorage); + + // Expected settings should be empty because the default is to read from the primary vendor + // settings location. + SettingsEntry expectedSettings = new SettingsEntry(); + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + + // Now switch to secondary vendor settings and assert proper settings. + provider.setBaseSettingsStorage(mSecondaryVendorSettingsStorage); + expectedSettings.mWindowingMode = WINDOWING_MODE_FULLSCREEN; + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + + // Switch back to primary and assert settings are empty again. + provider.setBaseSettingsStorage(mDefaultVendorSettingsStorage); + expectedSettings.mWindowingMode = WINDOWING_MODE_UNDEFINED; + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + } + + @Test + public void testReadingDisplaySettingsFromStorage_overrideSettingsTakePrecedenceOverVendor() { + final String displayIdentifier = mSecondaryDisplay.getDisplayInfo().uniqueId; + prepareOverrideDisplaySettings(displayIdentifier); + prepareSecondaryDisplaySettings(displayIdentifier); + + final DisplayWindowSettingsProvider provider = + new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage, + mOverrideSettingsStorage); + provider.setBaseSettingsStorage(mSecondaryVendorSettingsStorage); + + // The windowing mode should be set to WINDOWING_MODE_PINNED because the override settings + // take precedence over the vendor provided settings. + SettingsEntry expectedSettings = new SettingsEntry(); + expectedSettings.mWindowingMode = WINDOWING_MODE_PINNED; + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + } + + @Test public void testWritingDisplaySettingsToStorage() throws Exception { final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo(); // Write some settings to storage. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( - mBaseSettingsStorage, mOverrideSettingsStorage); + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); overrideSettings.mShouldShowSystemDecors = true; overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; @@ -195,7 +241,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { // Write some settings to storage. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( - mBaseSettingsStorage, mOverrideSettingsStorage); + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); overrideSettings.mShouldShowSystemDecors = true; overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; @@ -236,10 +282,29 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { mOverrideSettingsStorage.setReadStream(is); } + /** + * Prepares display settings and stores in {@link #mSecondaryVendorSettingsStorage}. Uses + * provided display identifier and stores windowingMode=WINDOWING_MODE_FULLSCREEN. + */ + private void prepareSecondaryDisplaySettings(String displayIdentifier) { + String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<display-settings>\n"; + if (displayIdentifier != null) { + contents += " <display\n" + + " name=\"" + displayIdentifier + "\"\n" + + " windowingMode=\"" + WINDOWING_MODE_FULLSCREEN + "\"/>\n"; + } + contents += "</display-settings>\n"; + + final InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); + mSecondaryVendorSettingsStorage.setReadStream(is); + } + private void readAndAssertExpectedSettings(DisplayContent displayContent, SettingsEntry expectedSettings) { final DisplayWindowSettingsProvider provider = - new DisplayWindowSettingsProvider(mBaseSettingsStorage, mOverrideSettingsStorage); + new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage, + mOverrideSettingsStorage); assertEquals(expectedSettings, provider.getSettings(displayContent.getDisplayInfo())); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java new file mode 100644 index 000000000000..eebc207d2d4f --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.view.Surface.ROTATION_90; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; +import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; +import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.SizeCompatTests.prepareUnresizable; +import static com.android.server.wm.SizeCompatTests.rotateDisplay; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.Display; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests for the Dual DisplayAreaGroup device behavior. + * + * Build/Install/Run: + * atest WmTests:DualDisplayAreaGroupPolicyTest + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { + private static final int FEATURE_FIRST_ROOT = FEATURE_VENDOR_FIRST; + private static final int FEATURE_FIRST_TASK_CONTAINER = FEATURE_DEFAULT_TASK_CONTAINER; + private static final int FEATURE_SECOND_ROOT = FEATURE_VENDOR_FIRST + 1; + private static final int FEATURE_SECOND_TASK_CONTAINER = FEATURE_VENDOR_FIRST + 2; + + private DualDisplayContent mDisplay; + private DisplayAreaGroup mFirstRoot; + private DisplayAreaGroup mSecondRoot; + private TaskDisplayArea mFirstTda; + private TaskDisplayArea mSecondTda; + private Task mFirstTask; + private Task mSecondTask; + private ActivityRecord mFirstActivity; + private ActivityRecord mSecondActivity; + + @Before + public void setUp() { + // Let the Display to be created with the DualDisplay policy. + final DisplayAreaPolicy.Provider policyProvider = new DualDisplayTestPolicyProvider(); + doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider(); + + // Display: 1920x1200 (landscape). First and second display are both 860x1200 (portrait). + mDisplay = (DualDisplayContent) new DualDisplayContent.Builder(mAtm, 1920, 1200).build(); + mFirstRoot = mDisplay.mFirstRoot; + mSecondRoot = mDisplay.mSecondRoot; + mFirstTda = mDisplay.getTaskDisplayArea(FEATURE_FIRST_TASK_CONTAINER); + mSecondTda = mDisplay.getTaskDisplayArea(FEATURE_SECOND_TASK_CONTAINER); + mFirstTask = new TaskBuilder(mSupervisor) + .setTaskDisplayArea(mFirstTda) + .setCreateActivity(true) + .build() + .getBottomMostTask(); + mSecondTask = new TaskBuilder(mSupervisor) + .setTaskDisplayArea(mSecondTda) + .setCreateActivity(true) + .build() + .getBottomMostTask(); + mFirstActivity = mFirstTask.getTopNonFinishingActivity(); + mSecondActivity = mSecondTask.getTopNonFinishingActivity(); + + spyOn(mDisplay); + spyOn(mFirstRoot); + spyOn(mSecondRoot); + } + + @Test + public void testNotIgnoreOrientationRequest_differentOrientationFromDisplay_reversesRequest() { + mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); + + assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); + assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); + + assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); + assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); + } + + @Test + public void testNotIgnoreOrientationRequest_onlyRespectsFocusedTaskDisplayArea() { + mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + // Second TDA is not focused, so Display won't get the request + prepareUnresizable(mSecondActivity, SCREEN_ORIENTATION_LANDSCAPE); + + assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); + + // First TDA is focused, so Display gets the request + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); + + assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); + } + + @Test + public void testIgnoreOrientationRequest_displayDoesNotReceiveOrientationChange() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); + + verify(mFirstRoot).onDescendantOrientationChanged(any()); + verify(mDisplay, never()).onDescendantOrientationChanged(any()); + } + + @Test + public void testLaunchPortraitApp_fillsDisplayAreaGroup() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); + final Rect dagBounds = new Rect(mFirstRoot.getBounds()); + final Rect taskBounds = new Rect(mFirstTask.getBounds()); + final Rect activityBounds = new Rect(mFirstActivity.getBounds()); + + // DAG is portrait (860x1200), so Task and Activity fill DAG. + assertThat(mFirstTask.isTaskLetterboxed()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + assertThat(taskBounds).isEqualTo(dagBounds); + assertThat(activityBounds).isEqualTo(taskBounds); + } + + @Test + public void testLaunchPortraitApp_sizeCompatAfterRotation() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); + final Rect dagBounds = new Rect(mFirstRoot.getBounds()); + final Rect activityBounds = new Rect(mFirstActivity.getBounds()); + + rotateDisplay(mDisplay, ROTATION_90); + final Rect newDagBounds = new Rect(mFirstRoot.getBounds()); + final Rect newTaskBounds = new Rect(mFirstTask.getBounds()); + final Rect activitySizeCompatBounds = new Rect(mFirstActivity.getBounds()); + final Rect activityConfigBounds = + new Rect(mFirstActivity.getConfiguration().windowConfiguration.getBounds()); + + // DAG is landscape (1200x860), Task fills parent + assertThat(mFirstTask.isTaskLetterboxed()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isTrue(); + assertThat(newDagBounds.width()).isEqualTo(dagBounds.height()); + assertThat(newDagBounds.height()).isEqualTo(dagBounds.width()); + assertThat(newTaskBounds).isEqualTo(newDagBounds); + + // Activity config bounds is unchanged, size compat bounds is (860x[860x860/1200=616]) + assertThat(mFirstActivity.getSizeCompatScale()).isLessThan(1f); + assertThat(activityConfigBounds.width()).isEqualTo(activityBounds.width()); + assertThat(activityConfigBounds.height()).isEqualTo(activityBounds.height()); + assertThat(activitySizeCompatBounds.height()).isEqualTo(newTaskBounds.height()); + assertThat(activitySizeCompatBounds.width()).isEqualTo( + newTaskBounds.height() * newTaskBounds.height() / newTaskBounds.width()); + } + + @Test + public void testLaunchLandscapeApp_taskIsLetterboxInDisplayAreaGroup() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); + final Rect dagBounds = new Rect(mFirstRoot.getBounds()); + final Rect taskBounds = new Rect(mFirstTask.getBounds()); + final Rect activityBounds = new Rect(mFirstActivity.getBounds()); + + // DAG is portrait (860x1200), so Task is letterbox (860x[860x860/1200=616]) + assertThat(mFirstTask.isTaskLetterboxed()).isTrue(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + assertThat(taskBounds.width()).isEqualTo(dagBounds.width()); + assertThat(taskBounds.height()) + .isEqualTo(dagBounds.width() * dagBounds.width() / dagBounds.height()); + assertThat(activityBounds).isEqualTo(taskBounds); + } + + @Test + public void testLaunchLandscapeApp_taskLetterboxBecomesActivityLetterboxAfterRotation() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); + final Rect dagBounds = new Rect(mFirstRoot.getBounds()); + final Rect activityBounds = new Rect(mFirstActivity.getBounds()); + + rotateDisplay(mDisplay, ROTATION_90); + final Rect newDagBounds = new Rect(mFirstRoot.getBounds()); + final Rect newTaskBounds = new Rect(mFirstTask.getBounds()); + final Rect newActivityBounds = new Rect(mFirstActivity.getBounds()); + + // DAG is landscape (1200x860), Task fills parent + // Task letterbox size + assertThat(mFirstTask.isTaskLetterboxed()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isTrue(); + assertThat(newDagBounds.width()).isEqualTo(dagBounds.height()); + assertThat(newDagBounds.height()).isEqualTo(dagBounds.width()); + assertThat(newTaskBounds).isEqualTo(newDagBounds); + + // Because we don't scale up, there is no size compat bounds and app bounds is the same as + // the previous bounds. + assertThat(mFirstActivity.hasSizeCompatBounds()).isFalse(); + assertThat(newActivityBounds.width()).isEqualTo(activityBounds.width()); + assertThat(newActivityBounds.height()).isEqualTo(activityBounds.height()); + } + + /** Display with two {@link DisplayAreaGroup}. Each of them take half of the screen. */ + private static class DualDisplayContent extends TestDisplayContent { + final DisplayAreaGroup mFirstRoot; + final DisplayAreaGroup mSecondRoot; + final Rect mLastDisplayBounds; + + /** Please use the {@link Builder} to create. */ + DualDisplayContent(RootWindowContainer rootWindowContainer, + Display display) { + super(rootWindowContainer, display); + + mFirstRoot = getGroupRoot(FEATURE_FIRST_ROOT); + mSecondRoot = getGroupRoot(FEATURE_SECOND_ROOT); + mLastDisplayBounds = new Rect(getBounds()); + updateDisplayAreaGroupBounds(); + } + + DisplayAreaGroup getGroupRoot(int rootFeatureId) { + DisplayArea da = getDisplayArea(rootFeatureId); + assertThat(da).isInstanceOf(DisplayAreaGroup.class); + return (DisplayAreaGroup) da; + } + + TaskDisplayArea getTaskDisplayArea(int tdaFeatureId) { + DisplayArea da = getDisplayArea(tdaFeatureId); + assertThat(da).isInstanceOf(TaskDisplayArea.class); + return (TaskDisplayArea) da; + } + + DisplayArea getDisplayArea(int featureId) { + final DisplayArea displayArea = + getItemFromDisplayAreas(da -> da.mFeatureId == featureId ? da : null); + assertThat(displayArea).isNotNull(); + return displayArea; + } + + @Override + public void onConfigurationChanged(Configuration newParentConfig) { + super.onConfigurationChanged(newParentConfig); + + final Rect curBounds = getBounds(); + if (mLastDisplayBounds != null && !mLastDisplayBounds.equals(curBounds)) { + mLastDisplayBounds.set(curBounds); + updateDisplayAreaGroupBounds(); + } + } + + /** Updates first and second {@link DisplayAreaGroup} to take half of the screen. */ + private void updateDisplayAreaGroupBounds() { + if (mFirstRoot == null || mSecondRoot == null) { + return; + } + + final Rect bounds = mLastDisplayBounds; + Rect groupBounds1, groupBounds2; + if (bounds.width() >= bounds.height()) { + groupBounds1 = new Rect(bounds.left, bounds.top, + (bounds.right + bounds.left) / 2, bounds.bottom); + + groupBounds2 = new Rect((bounds.right + bounds.left) / 2, bounds.top, + bounds.right, bounds.bottom); + } else { + groupBounds1 = new Rect(bounds.left, bounds.top, + bounds.right, (bounds.top + bounds.bottom) / 2); + + groupBounds2 = new Rect(bounds.left, + (bounds.top + bounds.bottom) / 2, bounds.right, bounds.bottom); + } + mFirstRoot.setBounds(groupBounds1); + mSecondRoot.setBounds(groupBounds2); + } + + static class Builder extends TestDisplayContent.Builder { + + Builder(ActivityTaskManagerService service, int width, int height) { + super(service, width, height); + } + + @Override + TestDisplayContent createInternal(Display display) { + return new DualDisplayContent(mService.mRootWindowContainer, display); + } + } + } + + /** Policy to create a dual {@link DisplayAreaGroup} policy in test. */ + private static class DualDisplayTestPolicyProvider implements DisplayAreaPolicy.Provider { + + @Override + public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, + RootDisplayArea root, DisplayArea.Tokens imeContainer) { + // Root + // Include FEATURE_WINDOWED_MAGNIFICATION because it will be used as the screen rotation + // layer + DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy = + new DisplayAreaPolicyBuilder.HierarchyBuilder(root) + .setImeContainer(imeContainer) + .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder( + wmService.mPolicy, + "WindowedMagnification", FEATURE_WINDOWED_MAGNIFICATION) + .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) + .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) + .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new) + .build()); + + // First + final RootDisplayArea firstRoot = new DisplayAreaGroup(wmService, "FirstRoot", + FEATURE_FIRST_ROOT); + final TaskDisplayArea firstTaskDisplayArea = new TaskDisplayArea(content, wmService, + "FirstTaskDisplayArea", FEATURE_FIRST_TASK_CONTAINER); + final List<TaskDisplayArea> firstTdaList = new ArrayList<>(); + firstTdaList.add(firstTaskDisplayArea); + DisplayAreaPolicyBuilder.HierarchyBuilder firstHierarchy = + new DisplayAreaPolicyBuilder.HierarchyBuilder(firstRoot) + .setTaskDisplayAreas(firstTdaList); + + // Second + final RootDisplayArea secondRoot = new DisplayAreaGroup(wmService, "SecondRoot", + FEATURE_SECOND_ROOT); + final TaskDisplayArea secondTaskDisplayArea = new TaskDisplayArea(content, wmService, + "SecondTaskDisplayArea", FEATURE_SECOND_TASK_CONTAINER); + final List<TaskDisplayArea> secondTdaList = new ArrayList<>(); + secondTdaList.add(secondTaskDisplayArea); + DisplayAreaPolicyBuilder.HierarchyBuilder secondHierarchy = + new DisplayAreaPolicyBuilder.HierarchyBuilder(secondRoot) + .setTaskDisplayAreas(secondTdaList); + + return new DisplayAreaPolicyBuilder() + .setRootHierarchy(rootHierarchy) + .addDisplayAreaGroupHierarchy(firstHierarchy) + .addDisplayAreaGroupHierarchy(secondHierarchy) + .build(wmService); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java index 91cfd4e6a89d..59d195b670a8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java @@ -18,7 +18,9 @@ package com.android.server.wm; import static android.view.InsetsState.ITYPE_IME; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.graphics.PixelFormat; @@ -64,4 +66,22 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { mImeProvider.scheduleShowImePostLayout(target); assertTrue(mImeProvider.isImeTargetFromDisplayContentAndImeSame()); } + + @Test + public void testIsImeShowing() { + WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime"); + makeWindowVisibleAndDrawn(ime); + mImeProvider.setWindow(ime, null, null); + + WindowState target = createWindow(null, TYPE_APPLICATION, "app"); + mDisplayContent.mInputMethodTarget = target; + mDisplayContent.mInputMethodControlTarget = target; + + mImeProvider.scheduleShowImePostLayout(target); + assertFalse(mImeProvider.isImeShowing()); + mImeProvider.checkShowImePostLayout(); + assertTrue(mImeProvider.isImeShowing()); + mImeProvider.setImeShowing(false); + assertFalse(mImeProvider.isImeShowing()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index da00198030e4..dd6e490b8282 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -92,7 +92,7 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mVisibleRequested = true; mActivity.setSavedState(null /* savedState */); mActivity.setState(Task.ActivityState.RESUMED, "testRestart"); - prepareUnresizable(1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); final Rect originalOverrideBounds = new Rect(mActivity.getBounds()); resizeDisplay(mTask.mDisplayContent, 600, 1200); @@ -115,7 +115,7 @@ public class SizeCompatTests extends WindowTestsBase { // Put app window into freeform and then make it a compat app. final Rect bounds = new Rect(100, 100, 400, 600); mTask.setBounds(bounds); - prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); assertEquals(bounds, mActivity.getBounds()); // The activity should be able to accept negative x position [-150, 100 - 150, 600]. @@ -145,7 +145,7 @@ public class SizeCompatTests extends WindowTestsBase { final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds(); final float aspectRatio = 1.2f; mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = aspectRatio; - prepareUnresizable(-1f, SCREEN_ORIENTATION_UNSPECIFIED); + prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED); final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds(); // The parent configuration doesn't change since the first resolved configuration, so the @@ -205,7 +205,7 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testFixedScreenBoundsWhenDisplaySizeChanged() { setUpDisplaySizeWithApp(1000, 2500); - prepareUnresizable(-1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); assertFitted(); final Rect origBounds = new Rect(mActivity.getBounds()); @@ -262,7 +262,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(displayWidth, 1000); final float maxAspect = 1.5f; - prepareUnresizable(maxAspect, SCREEN_ORIENTATION_LANDSCAPE); + prepareUnresizable(mActivity, maxAspect, SCREEN_ORIENTATION_LANDSCAPE); assertFitted(); final Rect bounds = mActivity.getBounds(); @@ -286,7 +286,7 @@ public class SizeCompatTests extends WindowTestsBase { public void testAspectRatioMatchParentBoundsAndImeAttachable() { setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2000) .setSystemDecorations(true).build()); - prepareUnresizable(2f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + prepareUnresizable(mActivity, 2f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); assertFitted(); rotateDisplay(mActivity.mDisplayContent, ROTATION_90); @@ -307,7 +307,7 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testMoveToDifferentOrientDisplay() { setUpDisplaySizeWithApp(1000, 2500); - prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); assertFitted(); final Rect configBounds = mActivity.getWindowConfiguration().getBounds(); @@ -352,7 +352,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500) .setNotch(notchHeight).build()); // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460]. - prepareUnresizable(1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); final Rect currentBounds = mActivity.getWindowConfiguration().getBounds(); final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds(); @@ -381,7 +381,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 2500); final float maxAspect = 1.4f; - prepareUnresizable(maxAspect, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, maxAspect, SCREEN_ORIENTATION_PORTRAIT); // The display aspect ratio 2.5 > 1.4 (max of activity), so the size is fitted. assertFitted(); @@ -415,7 +415,7 @@ public class SizeCompatTests extends WindowTestsBase { Configuration c = new Configuration(mTask.getRequestedOverrideConfiguration()); c.screenLayout = fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR; mTask.onRequestedOverrideConfigurationChanged(c); - prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED); + prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_UNSPECIFIED); // The initial configuration should inherit from parent. assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR, @@ -433,7 +433,7 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testResetNonVisibleActivity() { setUpDisplaySizeWithApp(1000, 2500); - prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED); + prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_UNSPECIFIED); final DisplayContent display = mTask.mDisplayContent; // Resize the display so the activity is in size compatibility mode. resizeDisplay(display, 900, 1800); @@ -472,7 +472,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 2000); ActivityRecord activity = mActivity; activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode"); - prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); assertFitted(); final ArrayList<IBinder> compatTokens = new ArrayList<>(); @@ -545,7 +545,7 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN); mActivity.mDisplayContent.mOpeningApps.add(mActivity); final float maxAspect = 1.8f; - prepareUnresizable(maxAspect, SCREEN_ORIENTATION_LANDSCAPE); + prepareUnresizable(mActivity, maxAspect, SCREEN_ORIENTATION_LANDSCAPE); assertFitted(); assertTrue(mActivity.isFixedRotationTransforming()); @@ -576,7 +576,7 @@ public class SizeCompatTests extends WindowTestsBase { assertFalse(statusBarController.isTransparentAllowed(w)); // Make the activity fill the display. - prepareUnresizable(10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE); + prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE); w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; // Refresh the letterbox. mActivity.mRootWindowContainer.performSurfacePlacement(); @@ -593,11 +593,11 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Portrait fixed app without max aspect. - prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); - final Rect displayBounds = mActivity.mDisplayContent.getBounds(); - final Rect taskBounds = mTask.getBounds(); - final Rect activityBounds = mActivity.getBounds(); + final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + final Rect taskBounds = new Rect(mTask.getBounds()); + final Rect activityBounds = new Rect(mActivity.getBounds()); // Display shouldn't be rotated. assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, @@ -622,15 +622,15 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Portrait fixed app without max aspect. - prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); - final Rect activityBounds = mActivity.getBounds(); + final Rect activityBounds = new Rect(mActivity.getBounds()); // Rotate display to portrait. rotateDisplay(mActivity.mDisplayContent, ROTATION_90); - final Rect displayBounds = mActivity.mDisplayContent.getBounds(); - final Rect newActivityBounds = mActivity.getBounds(); + final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + final Rect newActivityBounds = new Rect(mActivity.getBounds()); assertTrue(displayBounds.width() < displayBounds.height()); // App should be in size compat. @@ -647,10 +647,10 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Portrait fixed app without max aspect. - prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); - Rect displayBounds = mActivity.mDisplayContent.getBounds(); - Rect activityBounds = mActivity.getBounds(); + Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + Rect activityBounds = new Rect(mActivity.getBounds()); // App should launch in fullscreen. assertFalse(mTask.isTaskLetterboxed()); @@ -660,8 +660,8 @@ public class SizeCompatTests extends WindowTestsBase { // Rotate display to landscape. rotateDisplay(mActivity.mDisplayContent, ROTATION_90); - displayBounds = mActivity.mDisplayContent.getBounds(); - activityBounds = mActivity.getBounds(); + displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + activityBounds = new Rect(mActivity.getBounds()); assertTrue(displayBounds.width() > displayBounds.height()); // App should be in size compat. @@ -682,7 +682,7 @@ public class SizeCompatTests extends WindowTestsBase { display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Portrait fixed app without max aspect. - prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); assertTrue(mTask.isTaskLetterboxed()); assertFalse(mActivity.inSizeCompatMode()); @@ -701,9 +701,9 @@ public class SizeCompatTests extends WindowTestsBase { verify(mTask).onDescendantOrientationChanged(same(newActivity)); verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); - final Rect displayBounds = display.getBounds(); - final Rect taskBounds = mTask.getBounds(); - final Rect newActivityBounds = newActivity.getBounds(); + final Rect displayBounds = new Rect(display.getBounds()); + final Rect taskBounds = new Rect(mTask.getBounds()); + final Rect newActivityBounds = new Rect(newActivity.getBounds()); // Task and app bounds should be 700x1400 with the ratio as the display. assertTrue(mTask.isTaskLetterboxed()); @@ -722,7 +722,7 @@ public class SizeCompatTests extends WindowTestsBase { display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Portrait fixed app without max aspect. - prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); assertTrue(mTask.isTaskLetterboxed()); assertFalse(mActivity.inSizeCompatMode()); @@ -742,9 +742,9 @@ public class SizeCompatTests extends WindowTestsBase { verify(mTask).onDescendantOrientationChanged(same(newActivity)); verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); - final Rect displayBounds = display.getBounds(); - final Rect taskBounds = mTask.getBounds(); - final Rect newActivityBounds = newActivity.getBounds(); + final Rect displayBounds = new Rect(display.getBounds()); + final Rect taskBounds = new Rect(mTask.getBounds()); + final Rect newActivityBounds = new Rect(newActivity.getBounds()); // Task bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio. assertTrue(mTask.isTaskLetterboxed()); @@ -765,7 +765,7 @@ public class SizeCompatTests extends WindowTestsBase { display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Portrait fixed app. - prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); clearInvocations(mActivity); assertTrue(mTask.isTaskLetterboxed()); @@ -829,26 +829,31 @@ public class SizeCompatTests extends WindowTestsBase { displayContent.getConfiguration().uiMode); } + static void prepareUnresizable(ActivityRecord activity, int screenOrientation) { + prepareUnresizable(activity, -1 /* maxAspect */, screenOrientation); + } + /** - * Setup {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or + * Setups {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or * orientation. */ - private void prepareUnresizable(float maxAspect, int screenOrientation) { - mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE; - mActivity.mVisibleRequested = true; + static void prepareUnresizable(ActivityRecord activity, float maxAspect, + int screenOrientation) { + activity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE; + activity.mVisibleRequested = true; if (maxAspect >= 0) { - mActivity.info.maxAspectRatio = maxAspect; + activity.info.maxAspectRatio = maxAspect; } if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { - mActivity.info.screenOrientation = screenOrientation; - mActivity.setRequestedOrientation(screenOrientation); + activity.info.screenOrientation = screenOrientation; + activity.setRequestedOrientation(screenOrientation); } // Make sure to use the provided configuration to construct the size compat fields. - mActivity.clearSizeCompatMode(); - mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); + activity.clearSizeCompatMode(); + activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); // Make sure the display configuration reflects the change of activity. - if (mActivity.mDisplayContent.updateOrientation()) { - mActivity.mDisplayContent.sendNewConfiguration(); + if (activity.mDisplayContent.updateOrientation()) { + activity.mDisplayContent.sendNewConfiguration(); } } @@ -869,7 +874,7 @@ public class SizeCompatTests extends WindowTestsBase { assertFalse(mActivity.hasSizeCompatBounds()); } - private static Configuration rotateDisplay(DisplayContent display, int rotation) { + static Configuration rotateDisplay(DisplayContent display, int rotation) { final Configuration c = new Configuration(); display.getDisplayRotation().setRotation(rotation); display.computeScreenConfiguration(c); 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 536ef48065a5..e1bc90a2551c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -299,6 +299,7 @@ public class SystemServicesTestRule implements TestRule { doNothing().when(mWmService.mRoot).ensureActivitiesVisible(any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); spyOn(mWmService.mDisplayWindowSettings); + spyOn(mWmService.mDisplayWindowSettingsProvider); // Setup factory classes to prevent calls to native code. mTransaction = spy(StubTransaction.class); @@ -324,7 +325,7 @@ public class SystemServicesTestRule implements TestRule { final TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); // Set the default focused TDA. - display.setLastFocusedTaskDisplayArea(taskDisplayArea); + display.onLastFocusedTaskDisplayAreaChanged(taskDisplayArea); spyOn(taskDisplayArea); final Task homeStack = taskDisplayArea.getRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index d80f81642474..1c93e0f7e185 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -262,20 +262,50 @@ public class TaskDisplayAreaTests extends WindowTestsBase { // Activity on TDA1 is focused mDisplayContent.setFocusedApp(firstActivity); - assertThat(firstTaskDisplayArea.isLastFocused()).isTrue(); - assertThat(secondTaskDisplayArea.isLastFocused()).isFalse(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); // No focused app, TDA1 is still recorded as last focused. mDisplayContent.setFocusedApp(null); - assertThat(firstTaskDisplayArea.isLastFocused()).isTrue(); - assertThat(secondTaskDisplayArea.isLastFocused()).isFalse(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); // Activity on TDA2 is focused mDisplayContent.setFocusedApp(secondActivity); - assertThat(firstTaskDisplayArea.isLastFocused()).isFalse(); - assertThat(secondTaskDisplayArea.isLastFocused()).isTrue(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue(); + } + + @Test + public void testIsLastFocused_onlyCountIfTaskDisplayAreaHandlesOrientationRequest() { + final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", + FEATURE_VENDOR_FIRST); + final Task firstStack = firstTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final Task secondStack = secondTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm) + .setTask(firstStack).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setTask(secondStack).build(); + firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + + // Activity on TDA1 is focused, but TDA1 doesn't respect orientation request + mDisplayContent.setFocusedApp(firstActivity); + + assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); + + // Activity on TDA2 is focused, and TDA2 respects orientation request + mDisplayContent.setFocusedApp(secondActivity); + + assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index e95efe785e8c..ae85ceb72958 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -37,7 +37,8 @@ import android.view.DisplayInfo; class TestDisplayContent extends DisplayContent { - private TestDisplayContent(RootWindowContainer rootWindowContainer, Display display) { + /** Please use the {@link Builder} to create, visible for use in test builder overrides only. */ + TestDisplayContent(RootWindowContainer rootWindowContainer, Display display) { super(display, rootWindowContainer); // Normally this comes from display-properties as exposed by WM. Without that, just // hard-code to FULLSCREEN for tests. @@ -72,7 +73,7 @@ class TestDisplayContent extends DisplayContent { private boolean mCanRotate = true; private int mWindowingMode = WINDOWING_MODE_FULLSCREEN; private int mPosition = POSITION_BOTTOM; - private final ActivityTaskManagerService mService; + protected final ActivityTaskManagerService mService; private boolean mSystemDecorations = false; Builder(ActivityTaskManagerService service, int width, int height) { @@ -125,14 +126,16 @@ class TestDisplayContent extends DisplayContent { mInfo.logicalDensityDpi = dpi; return this; } + TestDisplayContent createInternal(Display display) { + return new TestDisplayContent(mService.mRootWindowContainer, display); + } TestDisplayContent build() { SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock); final int displayId = SystemServicesTestRule.sNextDisplayId++; final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, mInfo, DEFAULT_DISPLAY_ADJUSTMENTS); - final TestDisplayContent newDisplay = - new TestDisplayContent(mService.mRootWindowContainer, display); + final TestDisplayContent newDisplay = createInternal(display); // disable the normal system decorations final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy(); spyOn(displayPolicy); @@ -158,7 +161,7 @@ class TestDisplayContent extends DisplayContent { mService.mRootWindowContainer.addChild(newDisplay, mPosition); // Set the default focused TDA. - newDisplay.setLastFocusedTaskDisplayArea(newDisplay.getDefaultTaskDisplayArea()); + newDisplay.onLastFocusedTaskDisplayAreaChanged(newDisplay.getDefaultTaskDisplayArea()); return newDisplay; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index c4bcfdbcf89d..b0b8afd6c3a4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -948,6 +948,36 @@ public class WindowContainerTests extends WindowTestsBase { assertFalse(act.isAnimating(PARENTS)); } + @Test + public void testRegisterWindowContainerListener() { + final WindowContainer container = new WindowContainer(mWm); + container.mDisplayContent = mDisplayContent; + final TestWindowContainerListener listener = new TestWindowContainerListener(); + Configuration config = container.getConfiguration(); + Rect bounds = new Rect(0, 0, 10, 10); + config.windowConfiguration.setBounds(bounds); + config.densityDpi = 100; + container.onRequestedOverrideConfigurationChanged(config); + container.registerWindowContainerListener(listener); + + assertEquals(mDisplayContent, listener.mDisplayContent); + assertEquals(bounds, listener.mConfiguration.windowConfiguration.getBounds()); + assertEquals(100, listener.mConfiguration.densityDpi); + + container.onDisplayChanged(mDefaultDisplay); + assertEquals(listener.mDisplayContent, mDefaultDisplay); + + config = new Configuration(); + bounds = new Rect(0, 0, 20, 20); + config.windowConfiguration.setBounds(bounds); + config.densityDpi = 200; + + container.onRequestedOverrideConfigurationChanged(config); + + assertEquals(bounds, listener.mConfiguration.windowConfiguration.getBounds()); + assertEquals(200, listener.mConfiguration.densityDpi); + } + /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; @@ -1131,4 +1161,19 @@ public class WindowContainerTests extends WindowTestsBase { mSession.kill(); } } + + private static class TestWindowContainerListener implements WindowContainerListener { + private Configuration mConfiguration = new Configuration(); + private DisplayContent mDisplayContent; + + @Override + public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { + mConfiguration.setTo(overrideConfiguration); + } + + @Override + public void onDisplayChanged(DisplayContent dc) { + mDisplayContent = dc; + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java new file mode 100644 index 000000000000..67067ee08e41 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.app.IWindowToken; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Binder; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Build/Install/Run: + * atest WmTests:WindowContextListenerControllerTests + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class WindowContextListenerControllerTests extends WindowTestsBase { + private WindowContextListenerController mController; + + private static final int TEST_UID = 12345; + private static final int ANOTHER_UID = 1000; + + private final IBinder mClientToken = new Binder(); + private WindowContainer mContainer; + + @Before + public void setUp() { + mController = new WindowContextListenerController(); + mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); + } + + @Test + public void testRegisterWindowContextListener() { + mController.registerWindowContainerListener(mClientToken, mContainer, -1); + + assertEquals(1, mController.mListeners.size()); + + final IBinder clientToken = new Binder(); + mController.registerWindowContainerListener(clientToken, mContainer, -1); + + assertEquals(2, mController.mListeners.size()); + + final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY, + mDefaultDisplay); + mController.registerWindowContainerListener(mClientToken, container, -1); + + // The number of listeners doesn't increase since the listener just gets updated. + assertEquals(2, mController.mListeners.size()); + + WindowContextListenerController.WindowContextListenerImpl listener = + mController.mListeners.get(mClientToken); + assertEquals(container, listener.getWindowContainer()); + } + + @Test + public void testRegisterWindowContextListenerClientConfigPropagation() { + final TestWindowTokenClient clientToken = new TestWindowTokenClient(); + + final Configuration config1 = mContainer.getConfiguration(); + final Rect bounds1 = new Rect(0, 0, 10, 10); + config1.windowConfiguration.setBounds(bounds1); + config1.densityDpi = 100; + mContainer.onRequestedOverrideConfigurationChanged(config1); + + mController.registerWindowContainerListener(clientToken, mContainer, -1); + + assertEquals(bounds1, clientToken.mConfiguration.windowConfiguration.getBounds()); + assertEquals(config1.densityDpi, clientToken.mConfiguration.densityDpi); + assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId); + + // Update the WindowContainer. + final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY, + mDefaultDisplay); + final Configuration config2 = container.getConfiguration(); + final Rect bounds2 = new Rect(0, 0, 20, 20); + config2.windowConfiguration.setBounds(bounds2); + config2.densityDpi = 200; + container.onRequestedOverrideConfigurationChanged(config2); + + mController.registerWindowContainerListener(clientToken, container, -1); + + assertEquals(bounds2, clientToken.mConfiguration.windowConfiguration.getBounds()); + assertEquals(config2.densityDpi, clientToken.mConfiguration.densityDpi); + assertEquals(DEFAULT_DISPLAY, clientToken.mDisplayId); + + // Update the configuration of WindowContainer. + container.onRequestedOverrideConfigurationChanged(config1); + + assertEquals(bounds1, clientToken.mConfiguration.windowConfiguration.getBounds()); + assertEquals(config1.densityDpi, clientToken.mConfiguration.densityDpi); + + // Update the display of WindowContainer. + container.onDisplayChanged(mDisplayContent); + + assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId); + } + + @Test + public void testCanCallerRemoveListener_NullListener_ReturnFalse() { + assertFalse(mController.assertCallerCanRemoveListener(mClientToken, + true /* callerCanManagerAppTokens */, TEST_UID)); + } + + @Test + public void testCanCallerRemoveListener_CanManageAppTokens_ReturnTrue() { + mController.registerWindowContainerListener(mClientToken, mContainer, TEST_UID); + + assertTrue(mController.assertCallerCanRemoveListener(mClientToken, + true /* callerCanManagerAppTokens */, ANOTHER_UID)); + } + + @Test + public void testCanCallerRemoveListener_SameUid_ReturnTrue() { + mController.registerWindowContainerListener(mClientToken, mContainer, TEST_UID); + + assertTrue(mController.assertCallerCanRemoveListener(mClientToken, + false /* callerCanManagerAppTokens */, TEST_UID)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testCanCallerRemoveListener_DifferentUid_ThrowException() { + mController.registerWindowContainerListener(mClientToken, mContainer, TEST_UID); + + mController.assertCallerCanRemoveListener(mClientToken, + false /* callerCanManagerAppTokens */, ANOTHER_UID); + } + + private class TestWindowTokenClient extends IWindowToken.Stub { + private Configuration mConfiguration; + private int mDisplayId; + private boolean mRemoved; + + @Override + public void onConfigurationChanged(Configuration configuration, int displayId) { + mConfiguration = configuration; + mDisplayId = displayId; + } + + @Override + public void onWindowTokenRemoved() { + mRemoved = true; + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java index ba144dd367d9..4e1b3510bdaa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java @@ -20,7 +20,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; -import static android.provider.Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS; +import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -58,8 +58,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testForceDesktopModeOnExternalDisplays() { - try (SettingsSession forceDesktopModeSession = new - SettingsSession(DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS)) { + try (BoolSettingsSession forceDesktopModeSession = new + BoolSettingsSession(DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS)) { final boolean forceDesktopMode = !forceDesktopModeSession.getSetting(); final Uri forceDesktopModeUri = forceDesktopModeSession.setSetting(forceDesktopMode); mWm.mSettingsObserver.onChange(false, forceDesktopModeUri); @@ -70,8 +70,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testFreeformWindow() { - try (SettingsSession freeformWindowSession = new - SettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { + try (BoolSettingsSession freeformWindowSession = new + BoolSettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { final boolean curFreeformWindow = freeformWindowSession.getSetting(); final boolean newFreeformWindow = !curFreeformWindow; final Uri freeformWindowUri = freeformWindowSession.setSetting(newFreeformWindow); @@ -84,8 +84,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testFreeformWindow_valueChanged_updatesDisplaySettings() { - try (SettingsSession freeformWindowSession = new - SettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { + try (BoolSettingsSession freeformWindowSession = new + BoolSettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { final boolean curFreeformWindow = freeformWindowSession.getSetting(); final boolean newFreeformWindow = !curFreeformWindow; final Uri freeformWindowUri = freeformWindowSession.setSetting(newFreeformWindow); @@ -106,8 +106,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testForceResizableMode() { - try (SettingsSession forceResizableSession = new - SettingsSession(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES)) { + try (BoolSettingsSession forceResizableSession = new + BoolSettingsSession(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES)) { final boolean forceResizableMode = !forceResizableSession.getSetting(); final Uri forceResizableUri = forceResizableSession.setSetting(forceResizableMode); @@ -119,8 +119,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testEnableSizeCompatFreeform() { - try (SettingsSession enableSizeCompatFreeformSession = new - SettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) { + try (BoolSettingsSession enableSizeCompatFreeformSession = new + BoolSettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) { final boolean enableSizeCompatFreeform = !enableSizeCompatFreeformSession.getSetting(); final Uri enableSizeCompatFreeformUri = @@ -132,21 +132,22 @@ public class WindowManagerSettingsTests extends WindowTestsBase { } @Test - public void testEnabledIgnoreVendorDisplaySettings() { - try (SettingsSession ignoreVendorDisplaySettingsSession = new - SettingsSession(DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS)) { - final boolean ignoreVendorDisplaySettings = - !ignoreVendorDisplaySettingsSession.getSetting(); - final Uri ignoreVendorDisplaySettingUri = - ignoreVendorDisplaySettingsSession.setSetting(ignoreVendorDisplaySettings); + public void testChangeBaseDisplaySettingsPath() { + try (StringSettingsSession baseDisplaySettingsPathSession = new + StringSettingsSession(DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH)) { + final String path = baseDisplaySettingsPathSession.getSetting() + "-test"; + final Uri baseDisplaySettingsPathUri = baseDisplaySettingsPathSession.setSetting(path); clearInvocations(mWm.mRoot); clearInvocations(mWm.mDisplayWindowSettings); + clearInvocations(mWm.mDisplayWindowSettingsProvider); - mWm.mSettingsObserver.onChange(false /* selfChange */, ignoreVendorDisplaySettingUri); + mWm.mSettingsObserver.onChange(false /* selfChange */, baseDisplaySettingsPathUri); - assertEquals(mWm.mDisplayWindowSettingsProvider.getVendorSettingsIgnored(), - ignoreVendorDisplaySettings); + ArgumentCaptor<String> pathCaptor = ArgumentCaptor.forClass(String.class); + verify(mWm.mDisplayWindowSettingsProvider).setBaseSettingsFilePath( + pathCaptor.capture()); + assertEquals(path, pathCaptor.getValue()); ArgumentCaptor<DisplayContent> captor = ArgumentCaptor.forClass(DisplayContent.class); @@ -161,14 +162,14 @@ public class WindowManagerSettingsTests extends WindowTestsBase { } } - private class SettingsSession implements AutoCloseable { + private class BoolSettingsSession implements AutoCloseable { private static final int SETTING_VALUE_OFF = 0; private static final int SETTING_VALUE_ON = 1; private final String mSettingName; private final int mInitialValue; - SettingsSession(String name) { + BoolSettingsSession(String name) { mSettingName = name; mInitialValue = getSetting() ? SETTING_VALUE_ON : SETTING_VALUE_OFF; } @@ -192,4 +193,32 @@ public class WindowManagerSettingsTests extends WindowTestsBase { setSetting(mInitialValue == SETTING_VALUE_ON); } } + + private class StringSettingsSession implements AutoCloseable { + private final String mSettingName; + private final String mInitialValue; + + StringSettingsSession(String name) { + mSettingName = name; + mInitialValue = getSetting(); + } + + String getSetting() { + return Settings.Global.getString(getContentResolver(), mSettingName); + } + + Uri setSetting(String value) { + Settings.Global.putString(getContentResolver(), mSettingName, value); + return Settings.Global.getUriFor(mSettingName); + } + + private ContentResolver getContentResolver() { + return getInstrumentation().getTargetContext().getContentResolver(); + } + + @Override + public void close() { + setSetting(mInitialValue); + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index dba157e6ce06..f123bc16e52c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -824,7 +824,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final PictureInPictureParams p = new PictureInPictureParams.Builder() .setAspectRatio(new Rational(1, 2)).build(); - assertTrue(mWm.mAtmService.enterPictureInPictureMode(record.token, p)); + assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode( + record.token, p)); waitUntilHandlersIdle(); assertNotNull(o.mInfo); assertNotNull(o.mInfo.pictureInPictureParams); @@ -845,14 +846,15 @@ public class WindowOrganizerTests extends WindowTestsBase { final ActivityRecord record = makePipableActivity(); final PictureInPictureParams p = new PictureInPictureParams.Builder() .setAspectRatio(new Rational(1, 2)).build(); - assertTrue(mWm.mAtmService.enterPictureInPictureMode(record.token, p)); + assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode( + record.token, p)); waitUntilHandlersIdle(); assertNotNull(o.mInfo); assertNotNull(o.mInfo.pictureInPictureParams); final PictureInPictureParams p2 = new PictureInPictureParams.Builder() .setAspectRatio(new Rational(3, 4)).build(); - mWm.mAtmService.setPictureInPictureParams(record.token, p2); + mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2); waitUntilHandlersIdle(); assertNotNull(o.mChangedInfo); assertNotNull(o.mChangedInfo.pictureInPictureParams); @@ -920,7 +922,7 @@ public class WindowOrganizerTests extends WindowTestsBase { assertTrue(stack2.isOrganized()); // Verify a back pressed does not call the organizer - mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); + mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); verify(organizer, never()).onBackPressedOnTaskRoot(any()); // Enable intercepting back @@ -928,7 +930,7 @@ public class WindowOrganizerTests extends WindowTestsBase { stack.mRemoteToken.toWindowContainerToken(), true); // Verify now that the back press does call the organizer - mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); + mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); // Disable intercepting back @@ -936,7 +938,7 @@ public class WindowOrganizerTests extends WindowTestsBase { stack.mRemoteToken.toWindowContainerToken(), false); // Verify now that the back press no longer calls the organizer - mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); + mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 14a62d1a4ff0..d64b46d925ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -49,6 +49,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; +import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -354,6 +355,13 @@ class WindowTestsBase extends SystemServiceTestsBase { } } + static void makeWindowVisibleAndDrawn(WindowState... windows) { + makeWindowVisible(windows); + for (WindowState win : windows) { + win.mWinAnimator.mDrawState = HAS_DRAWN; + } + } + /** Creates a {@link TaskDisplayArea} right above the default one. */ static TaskDisplayArea createTaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name, int displayAreaFeature) { 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 e0785c13a336..462df300fdfd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -231,7 +231,7 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testWindowAttachedWithOptions() { - BiFunction<WindowToken, Bundle, RootDisplayArea> selectFunc = + BiFunction<Integer, Bundle, RootDisplayArea> selectFunc = ((DisplayAreaPolicyBuilder.Result) mDisplayContent.mDisplayAreaPolicy) .mSelectRootForWindowFunc; spyOn(selectFunc); @@ -241,7 +241,7 @@ public class WindowTokenTests extends WindowTestsBase { true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); - verify(selectFunc).apply(token1, null); + verify(selectFunc).apply(token1.windowType, null); final Bundle options = new Bundle(); final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), @@ -249,6 +249,6 @@ public class WindowTokenTests extends WindowTestsBase { true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, false /* fromClientToken */, options /* options */); - verify(selectFunc).apply(token2, options); + verify(selectFunc).apply(token2.windowType, options); } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 06c23de84ac3..b5bb2a81a1cd 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -49,7 +49,6 @@ import android.hardware.soundtrigger.SoundTrigger.ModelParamRange; import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; import android.media.permission.Identity; -import android.media.permission.IdentityContext; import android.media.permission.PermissionUtil; import android.media.permission.SafeCloseable; import android.os.Binder; @@ -92,7 +91,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; import com.android.server.pm.UserManagerInternal; -import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; import com.android.server.soundtrigger.SoundTriggerInternal; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.ActivityTaskManagerInternal; @@ -141,10 +140,10 @@ public class VoiceInteractionManagerService extends SystemService { mUserManagerInternal = Objects.requireNonNull( LocalServices.getService(UserManagerInternal.class)); - PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService( - PermissionManagerServiceInternal.class); + LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService( + LegacyPermissionManagerInternal.class); permissionManagerInternal.setVoiceInteractionPackagesProvider( - new PermissionManagerServiceInternal.PackagesProvider() { + new LegacyPermissionManagerInternal.PackagesProvider() { @Override public String[] getPackages(int userId) { mServiceStub.initForUser(userId); diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java index 3104c7e7e0a1..1a0e5269d51a 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java +++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java @@ -34,6 +34,7 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; +import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.Log; @@ -42,15 +43,20 @@ import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.BackgroundDexOptService; +import com.android.server.pm.PackageManagerService; import com.android.server.wm.ActivityMetricsLaunchObserver; import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto; import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature; import com.android.server.wm.ActivityMetricsLaunchObserverRegistry; import com.android.server.wm.ActivityTaskManagerInternal; +import java.time.Duration; import java.util.ArrayList; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BooleanSupplier; import java.util.HashMap; +import java.util.List; /** * System-server-local proxy into the {@code IIorap} native service. @@ -65,6 +71,7 @@ public class IorapForwardingService extends SystemService { /** $> adb shell 'setprop iorapd.forwarding_service.wtf_crash true' */ private static boolean WTF_CRASH = SystemProperties.getBoolean( "iorapd.forwarding_service.wtf_crash", false); + private static final Duration TIMEOUT = Duration.ofSeconds(600L); // "Unique" job ID from the service name. Also equal to 283673059. public static final int JOB_ID_IORAPD = encodeEnglishAlphabetStringIntoInt("iorapd"); @@ -80,6 +87,12 @@ public class IorapForwardingService extends SystemService { private volatile IorapdJobService mJobService; // Write-once (null -> non-null forever). private volatile static IorapForwardingService sSelfService; // Write once (null -> non-null). + + /** + * Atomics set to true if the JobScheduler requests an abort. + */ + private final AtomicBoolean mAbortIdleCompilation = new AtomicBoolean(false); + /** * Initializes the system service. * <p> @@ -154,9 +167,27 @@ public class IorapForwardingService extends SystemService { @VisibleForTesting protected boolean isIorapEnabled() { + // These two mendel flags should match those in iorapd native process + // system/iorapd/src/common/property.h + boolean isTracingEnabled = + getMendelFlag("iorap_perfetto_enable", "iorapd.perfetto.enable", false); + boolean isReadAheadEnabled = + getMendelFlag("iorap_readahead_enable", "iorapd.readahead.enable", false); // Same as the property in iorapd.rc -- disabling this will mean the 'iorapd' binder process // never comes up, so all binder connections will fail indefinitely. - return IS_ENABLED; + return IS_ENABLED && (isTracingEnabled || isReadAheadEnabled); + } + + private boolean getMendelFlag(String mendelFlag, String sysProperty, boolean defaultValue) { + // TODO(yawanng) use DeviceConfig to get mendel property. + // DeviceConfig doesn't work and the reason is not clear. + // Provider service is already up before IORapForwardService. + String mendelProperty = "persist.device_config." + + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT + + "." + + mendelFlag; + return SystemProperties.getBoolean(mendelProperty, + SystemProperties.getBoolean(sysProperty, defaultValue)); } //</editor-fold> @@ -239,7 +270,9 @@ public class IorapForwardingService extends SystemService { // // TODO: it would be good to get nodified of 'adb shell stop iorapd' to avoid // printing this warning. - Log.w(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime); + if (DEBUG) { + Log.v(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime); + } // Use a handler instead of Thread#sleep to avoid backing up the binder thread // when this is called from the death recipient callback. @@ -275,7 +308,9 @@ public class IorapForwardingService extends SystemService { // Connect to the native binder service. mIorapRemote = provideIorapRemote(); if (mIorapRemote == null) { - Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?"); + if (DEBUG) { + Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?"); + } return false; } invokeRemote(mIorapRemote, @@ -542,32 +577,86 @@ public class IorapForwardingService extends SystemService { // Tell iorapd to start a background job. Log.d(TAG, "Starting background job: " + params.toString()); - // We wait until that job's sequence ID returns to us with 'Completed', - RequestId request; - synchronized (mLock) { - // TODO: would be cleaner if we got the request from the 'invokeRemote' function. - // Better yet, consider a Pair<RequestId, Future<TaskResult>> or similar. - request = RequestId.nextValueForSequence(); - mRunningJobs.put(request, params); - } + mAbortIdleCompilation.set(false); + // PackageManagerService starts before IORap service. + PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package"); + List<String> pkgs = pm.getAllPackages(); + runIdleCompilationAsync(params, pkgs); + return true; + } - if (!invokeRemote(mIorapRemote, (IIorap remote) -> - remote.onJobScheduledEvent(request, - JobScheduledEvent.createIdleMaintenance( - JobScheduledEvent.TYPE_START_JOB, - params)) - )) { - synchronized (mLock) { - mRunningJobs.remove(request); // Avoid memory leaks. + private void runIdleCompilationAsync(final JobParameters params, final List<String> pkgs) { + new Thread("IORap_IdleCompilation") { + @Override + public void run() { + for (int i = 0; i < pkgs.size(); i++) { + String pkg = pkgs.get(i); + if (mAbortIdleCompilation.get()) { + Log.i(TAG, "The idle compilation is aborted"); + return; + } + + // We wait until that job's sequence ID returns to us with 'Completed', + RequestId request; + synchronized (mLock) { + // TODO: would be cleaner if we got the request from the + // 'invokeRemote' function. Better yet, consider + // a Pair<RequestId, Future<TaskResult>> or similar. + request = RequestId.nextValueForSequence(); + mRunningJobs.put(request, params); + } + + Log.i(TAG, String.format("IORap compile package: %s, [%d/%d]", + pkg, i + 1, pkgs.size())); + boolean shouldUpdateVersions = (i == 0); + if (!invokeRemote(mIorapRemote, (IIorap remote) -> + remote.onJobScheduledEvent(request, + JobScheduledEvent.createIdleMaintenance( + JobScheduledEvent.TYPE_START_JOB, + params, + pkg, + shouldUpdateVersions)))) { + synchronized (mLock) { + mRunningJobs.remove(request); // Avoid memory leaks. + } + } + + // Wait until the job is complete and removed from the running jobs. + retryWithTimeout(TIMEOUT, () -> { + synchronized (mLock) { + return !mRunningJobs.containsKey(request); + } + }); + } + + // Finish the job after all packages are compiled. + if (mProxy != null) { + mProxy.jobFinished(params, /*reschedule*/false); + } } + }.start(); + } - // Something went wrong on the remote side. Treat the job as being - // 'already finished' (i.e. immediately release wake lock). - return false; - } + /** Retry until timeout. */ + private boolean retryWithTimeout(final Duration timeout, BooleanSupplier supplier) { + long totalSleepTimeMs = 0L; + long sleepIntervalMs = 10L; + while (true) { + if (supplier.getAsBoolean()) { + return true; + } + try { + TimeUnit.MILLISECONDS.sleep(sleepIntervalMs); + } catch (InterruptedException e) { + Log.e(TAG, e.getMessage()); + return false; + } - // True -> keep the wakelock acquired until #jobFinished is called. - return true; + totalSleepTimeMs += sleepIntervalMs; + if (totalSleepTimeMs > timeout.toMillis()) { + return false; + } + } } // Called by system to prematurely stop the job. @@ -575,32 +664,7 @@ public class IorapForwardingService extends SystemService { public boolean onStopJob(JobParameters params) { // As this is unexpected behavior, print a warning. Log.w(TAG, "onStopJob(params=" + params.toString() + ")"); - - // No longer track this job (avoids a memory leak). - boolean wasTracking = false; - synchronized (mLock) { - for (HashMap.Entry<RequestId, JobParameters> entry : mRunningJobs.entrySet()) { - if (entry.getValue().getJobId() == params.getJobId()) { - mRunningJobs.remove(entry.getKey()); - wasTracking = true; - } - } - } - - // Notify iorapd to stop (abort) the job. - if (wasTracking) { - invokeRemote(mIorapRemote, (IIorap remote) -> - remote.onJobScheduledEvent(RequestId.nextValueForSequence(), - JobScheduledEvent.createIdleMaintenance( - JobScheduledEvent.TYPE_STOP_JOB, - params)) - ); - } else { - // Even weirder. This could only be considered "correct" if iorapd reported success - // concurrently to the JobService requesting an onStopJob. - Log.e(TAG, "Untracked onStopJob request"); // see above Log.w for the params. - } - + mAbortIdleCompilation.set(true); // Yes, retry the job at a later time no matter what. return true; @@ -626,18 +690,6 @@ public class IorapForwardingService extends SystemService { } Log.d(TAG, "Finished background job: " + jobParameters.toString()); - - // Job is successful and periodic. Do not 'reschedule' according to the back-off - // criteria. - // - // This releases the wakelock that was acquired in #onStartJob. - - IorapdJobServiceProxy proxy = mProxy; - if (proxy != null) { - proxy.jobFinished(jobParameters, /*reschedule*/false); - } - // Cannot call 'jobFinished' on 'this' because it was not constructed - // from the JobService, so it would get an NPE when calling mEngine. } public void onIorapdDisconnected() { diff --git a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java index 2055b206dd7a..b91dd71fd28c 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java +++ b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java @@ -55,6 +55,10 @@ public class JobScheduledEvent implements Parcelable { /** @see JobParameters#getJobId() */ public final int jobId; + public final String packageName; + + public final boolean shouldUpdateVersions; + /** Device is 'idle' and it's charging (plugged in). */ public static final int SORT_IDLE_MAINTENANCE = 0; private static final int SORT_MAX = 0; @@ -77,14 +81,22 @@ public class JobScheduledEvent implements Parcelable { * Only the job ID is retained from {@code jobParams}, all other param info is dropped. */ @NonNull - public static JobScheduledEvent createIdleMaintenance(@Type int type, JobParameters jobParams) { - return new JobScheduledEvent(type, jobParams.getJobId(), SORT_IDLE_MAINTENANCE); + public static JobScheduledEvent createIdleMaintenance( + @Type int type, JobParameters jobParams, String packageName, boolean shouldUpdateVersions) { + return new JobScheduledEvent( + type, jobParams.getJobId(), SORT_IDLE_MAINTENANCE, packageName, shouldUpdateVersions); } - private JobScheduledEvent(@Type int type, int jobId, @Sort int sort) { + private JobScheduledEvent(@Type int type, + int jobId, + @Sort int sort, + String packageName, + boolean shouldUpdateVersions) { this.type = type; this.jobId = jobId; this.sort = sort; + this.packageName = packageName; + this.shouldUpdateVersions = shouldUpdateVersions; checkConstructorArguments(); } @@ -108,12 +120,16 @@ public class JobScheduledEvent implements Parcelable { private boolean equals(JobScheduledEvent other) { return type == other.type && jobId == other.jobId && - sort == other.sort; + sort == other.sort && + packageName.equals(other.packageName) && + shouldUpdateVersions == other.shouldUpdateVersions; } @Override public String toString() { - return String.format("{type: %d, jobId: %d, sort: %d}", type, jobId, sort); + return String.format( + "{type: %d, jobId: %d, sort: %d, packageName: %s, shouldUpdateVersions %b}", + type, jobId, sort, packageName, shouldUpdateVersions); } //<editor-fold desc="Binder boilerplate"> @@ -122,6 +138,8 @@ public class JobScheduledEvent implements Parcelable { out.writeInt(type); out.writeInt(jobId); out.writeInt(sort); + out.writeString(packageName); + out.writeBoolean(shouldUpdateVersions); // We do not parcel the entire JobParameters here because there is no C++ equivalent // of that class [which the iorapd side of the binder interface requires]. @@ -131,6 +149,8 @@ public class JobScheduledEvent implements Parcelable { this.type = in.readInt(); this.jobId = in.readInt(); this.sort = in.readInt(); + this.packageName = in.readString(); + this.shouldUpdateVersions = in.readBoolean(); checkConstructorArguments(); } diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING index c9903f9fa901..1963ff3cabc7 100644 --- a/telecomm/TEST_MAPPING +++ b/telecomm/TEST_MAPPING @@ -23,7 +23,9 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] - }, + } + ], + "presubmit-large": [ { "name": "CtsTelecomTestCases", "options": [ diff --git a/telecomm/java/android/telecom/BluetoothCallQualityReport.java b/telecomm/java/android/telecom/BluetoothCallQualityReport.java new file mode 100644 index 000000000000..10339a818205 --- /dev/null +++ b/telecomm/java/android/telecom/BluetoothCallQualityReport.java @@ -0,0 +1,256 @@ +/* + * 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.telecom; + +import android.annotation.ElapsedRealtimeLong; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class represents the quality report that bluetooth framework sends + * whenever there's a bad voice quality is detected from their side. + * It is sent as part of a call event via {@link Call#sendCallEvent(String, Bundle)} + * associated with extra EXTRA_BLUETOOTH_CALL_QUALITY_REPORT. + * Note that this report will be sent only during an active voice/voip call. + * @hide + */ +@SystemApi +public final class BluetoothCallQualityReport implements Parcelable { + + /** + * Event that is sent via {@link Call#sendCallEvent(String, Bundle)} for a call quality report + */ + public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT = + "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT"; + + /** + * Extra key sent with {@link Call#sendCallEvent(String, Bundle)} + */ + public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT = + "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT"; + + private final long mSentTimestampMillis; + private final boolean mChoppyVoice; + private final int mRssiDbm; + private final int mSnrDb; + private final int mRetransmittedPacketsCount; + private final int mPacketsNotReceivedCount; + private final int mNegativeAcknowledgementCount; + + /** + * @return Time in milliseconds since the epoch. Designates when report was sent. + * Used to determine whether this report arrived too late to be useful. + */ + public @ElapsedRealtimeLong long getSentTimestampMillis() { + return mSentTimestampMillis; + } + + /** + * @return {@code true} if bluetooth hardware detects voice is choppy + */ + public boolean isChoppyVoice() { + return mChoppyVoice; + } + + /** + * @return Received Signal Strength Indication (RSSI) value in dBm. + * This value shall be an absolute received signal strength value. + */ + public @IntRange(from = -127, to = 20) int getRssiDbm() { + return mRssiDbm; + } + + /** + * @return Signal-to-Noise Ratio (SNR) value in dB. + * The controller shall provide the average SNR of all the channels currently used by the link. + */ + public int getSnrDb() { + return mSnrDb; + } + + /** + * @return The number of retransmissions since the last event. + * This count shall be reset after it is reported. + */ + public @IntRange(from = 0) int getRetransmittedPacketsCount() { + return mRetransmittedPacketsCount; + } + + /** + * @return No RX count since the last event. + * The count increases when no packet is received at the scheduled time slot or the received + * packet is corrupted. + * This count shall be reset after it is reported. + */ + public @IntRange(from = 0) int getPacketsNotReceivedCount() { + return mPacketsNotReceivedCount; + } + + /** + * @return NAK (Negative Acknowledge) count since the last event. + * This count shall be reset after it is reported. + */ + public @IntRange(from = 0) int getNegativeAcknowledgementCount() { + return mNegativeAcknowledgementCount; + } + + // + // Parcelable implementation + // + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeLong(mSentTimestampMillis); + out.writeBoolean(mChoppyVoice); + out.writeInt(mRssiDbm); + out.writeInt(mSnrDb); + out.writeInt(mRetransmittedPacketsCount); + out.writeInt(mPacketsNotReceivedCount); + out.writeInt(mNegativeAcknowledgementCount); + } + + public static final @android.annotation.NonNull Creator<BluetoothCallQualityReport> CREATOR = + new Creator<BluetoothCallQualityReport>() { + @Override + public BluetoothCallQualityReport createFromParcel(Parcel in) { + return new BluetoothCallQualityReport(in); + } + + @Override + public BluetoothCallQualityReport[] newArray(int size) { + return new BluetoothCallQualityReport[size]; + } + }; + + /** + * Builder class for {@link ConnectionRequest} + */ + public static final class Builder { + private long mSentTimestampMillis; + private boolean mChoppyVoice; + private int mRssiDbm; + private int mSnrDb; + private int mRetransmittedPacketsCount; + private int mPacketsNotReceivedCount; + private int mNegativeAcknowledgementCount; + + public Builder() { } + + /** + * Set the time when report was sent in milliseconds since the epoch. + * @param sentTimestampMillis + */ + public @NonNull Builder setSentTimestampMillis(long sentTimestampMillis) { + mSentTimestampMillis = sentTimestampMillis; + return this; + } + + /** + * Set if bluetooth hardware detects voice is choppy + * @param choppyVoice + */ + public @NonNull Builder setChoppyVoice(boolean choppyVoice) { + mChoppyVoice = choppyVoice; + return this; + } + + /** + * Set Received Signal Strength Indication (RSSI) value in dBm. + * @param rssiDbm + */ + public @NonNull Builder setRssiDbm(int rssiDbm) { + mRssiDbm = rssiDbm; + return this; + } + + /** + * Set Signal-to-Noise Ratio (SNR) value in dB. + * @param snrDb + */ + public @NonNull Builder setSnrDb(int snrDb) { + mSnrDb = snrDb; + return this; + } + + /** + * Set The number of retransmissions since the last event. + * @param retransmittedPacketsCount + */ + public @NonNull Builder setRetransmittedPacketsCount( + int retransmittedPacketsCount) { + mRetransmittedPacketsCount = retransmittedPacketsCount; + return this; + } + + /** + * Set No RX count since the last event. + * @param packetsNotReceivedCount + */ + public @NonNull Builder setPacketsNotReceivedCount( + int packetsNotReceivedCount) { + mPacketsNotReceivedCount = packetsNotReceivedCount; + return this; + } + + /** + * Set NAK (Negative Acknowledge) count since the last event. + * @param negativeAcknowledgementCount + */ + public @NonNull Builder setNegativeAcknowledgementCount( + int negativeAcknowledgementCount) { + mNegativeAcknowledgementCount = negativeAcknowledgementCount; + return this; + } + + /** + * Build the {@link BluetoothCallQualityReport} + * @return Result of the builder + */ + public @NonNull BluetoothCallQualityReport build() { + return new BluetoothCallQualityReport(this); + } + } + + private BluetoothCallQualityReport(Parcel in) { + mSentTimestampMillis = in.readLong(); + mChoppyVoice = in.readBoolean(); + mRssiDbm = in.readInt(); + mSnrDb = in.readInt(); + mRetransmittedPacketsCount = in.readInt(); + mPacketsNotReceivedCount = in.readInt(); + mNegativeAcknowledgementCount = in.readInt(); + } + + private BluetoothCallQualityReport(Builder builder) { + mSentTimestampMillis = builder.mSentTimestampMillis; + mChoppyVoice = builder.mChoppyVoice; + mRssiDbm = builder.mRssiDbm; + mSnrDb = builder.mSnrDb; + mRetransmittedPacketsCount = builder.mRetransmittedPacketsCount; + mPacketsNotReceivedCount = builder.mPacketsNotReceivedCount; + mNegativeAcknowledgementCount = builder.mNegativeAcknowledgementCount; + } +} diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 960b0df40061..5b03863efc7d 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -268,10 +268,69 @@ public class TelecomManager { /** * Optional extra for {@link android.content.Intent#ACTION_CALL} containing a string call * subject which will be associated with an outgoing call. Should only be specified if the - * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT}. + * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT} + * or {@link PhoneAccount#CAPABILITY_CALL_COMPOSER}. */ public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; + // Values for EXTRA_PRIORITY + /** + * Indicates the call composer call priority is normal. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final int PRIORITY_NORMAL = 0; + + /** + * Indicates the call composer call priority is urgent. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final int PRIORITY_URGENT = 1; + + /** + * Extra for the call composer call priority, either {@link #PRIORITY_NORMAL} or + * {@link #PRIORITY_URGENT}. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY"; + + /** + * Extra for the call composer call location, an {@link android.location.Location} parcelable + * class to represent the geolocation as a latitude and longitude pair. + * + * Reference: RCC.20 Section 2.4.3.2 + */ + public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION"; + + /** + * A boolean extra set on incoming calls to indicate that the call has a picture specified. + * Given that image download could take a (short) time, the EXTRA is set immediately upon + * adding the call to the Dialer app, this allows the Dialer app to reserve space for an image + * if one is expected. The EXTRA may be unset if the image download ends up failing for some + * reason. + */ + public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE"; + + /** + * A URI representing the picture that was downloaded when a call is received. + * This is a content URI within the call log provider which can be used to open a file + * descriptor. This could be set a short time after a call is added to the Dialer app if the + * download is delayed for some reason. The Dialer app will receive a callback via + * {@link Call.Callback#onDetailsChanged} when this value has changed. + * + * Reference: RCC.20 Section 2.4.3.2 + */ + public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE"; + + // TODO(hallliu), This UUID is obtained from TelephonyManager#uploadCallComposerPicture. + /** + * A ParcelUuid used as a token to represent a picture that was uploaded prior to the call + * being placed. + */ + public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE"; + /** * The extra used by a {@link ConnectionService} to provide the handle of the caller that * has initiated a new incoming call. diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index baee4736b527..0939bf02a039 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4012,6 +4012,17 @@ public class CarrierConfigManager { "default_preferred_apn_name_string"; /** + * Indicates if the carrier supports call composer. + */ + public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; + + /** + * Indicates the carrier server url that serves the call composer picture. + */ + public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING = + "call_composer_picture_server_url_string"; + + /** * For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values * for IPv4 and IPv6 if both are sent. * TODO: remove in later release @@ -4563,6 +4574,8 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); + sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false); + sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, ""); sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); } diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java index b381ccecde47..189a4b898886 100644 --- a/telephony/java/android/telephony/CellInfo.java +++ b/telephony/java/android/telephony/CellInfo.java @@ -21,7 +21,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.radio.V1_4.CellInfo.Info; -import android.hardware.radio.V1_5.CellInfo.CellInfoRatSpecificInfo; import android.os.Parcel; import android.os.Parcelable; @@ -352,6 +351,13 @@ public abstract class CellInfo implements Parcelable { } /** @hide */ + protected CellInfo(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + this.mRegistered = ci.registered; + this.mTimeStamp = timeStamp; + this.mCellConnectionStatus = ci.connectionStatus; + } + + /** @hide */ public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) { if (ci == null) return null; switch(ci.cellInfoType) { @@ -395,17 +401,49 @@ public abstract class CellInfo implements Parcelable { public static CellInfo create(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) { if (ci == null) return null; switch (ci.ratSpecificInfo.getDiscriminator()) { - case CellInfoRatSpecificInfo.hidl_discriminator.gsm: + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.gsm: + return new CellInfoGsm(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.cdma: + return new CellInfoCdma(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.lte: + return new CellInfoLte(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.wcdma: + return new CellInfoWcdma(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.tdscdma: + return new CellInfoTdscdma(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.nr: + return new CellInfoNr(ci, timeStamp); + default: return null; + } + } + + /** @hide */ + public static CellInfo create(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + if (ci == null) return null; + switch (ci.ratSpecificInfo.getDiscriminator()) { + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.gsm: return new CellInfoGsm(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.cdma: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.cdma: return new CellInfoCdma(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.lte: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.lte: return new CellInfoLte(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.wcdma: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.wcdma: return new CellInfoWcdma(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.tdscdma: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.tdscdma: return new CellInfoTdscdma(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.nr: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.nr: return new CellInfoNr(ci, timeStamp); default: return null; } diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java index 3ce99facfdb1..dbb30d29eb87 100644 --- a/telephony/java/android/telephony/CellInfoCdma.java +++ b/telephony/java/android/telephony/CellInfoCdma.java @@ -87,6 +87,15 @@ public final class CellInfoCdma extends CellInfo implements Parcelable { new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); } + /** @hide */ + public CellInfoCdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_2.CellInfoCdma cic = ci.ratSpecificInfo.cdma(); + mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma); + mCellSignalStrengthCdma = + new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); + } + /** * @return a {@link CellIdentityCdma} instance. */ diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java index e296e613362a..e1d996e87fcf 100644 --- a/telephony/java/android/telephony/CellInfoGsm.java +++ b/telephony/java/android/telephony/CellInfoGsm.java @@ -82,6 +82,14 @@ public final class CellInfoGsm extends CellInfo implements Parcelable { mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); } + /** @hide */ + public CellInfoGsm(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_5.CellInfoGsm cig = ci.ratSpecificInfo.gsm(); + mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm); + mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); + } + /** * @return a {@link CellIdentityGsm} instance. */ diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java index 6f812341756b..39b320afdac9 100644 --- a/telephony/java/android/telephony/CellInfoLte.java +++ b/telephony/java/android/telephony/CellInfoLte.java @@ -91,6 +91,15 @@ public final class CellInfoLte extends CellInfo implements Parcelable { mCellConfig = new CellConfigLte(); } + /** @hide */ + public CellInfoLte(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_6.CellInfoLte cil = ci.ratSpecificInfo.lte(); + mCellIdentityLte = new CellIdentityLte(cil.cellIdentityLte); + mCellSignalStrengthLte = new CellSignalStrengthLte(cil.signalStrengthLte); + mCellConfig = new CellConfigLte(); + } + /** * @return a {@link CellIdentityLte} instance. */ diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java index e01e8f0d5b51..12e6a38bfdc0 100644 --- a/telephony/java/android/telephony/CellInfoNr.java +++ b/telephony/java/android/telephony/CellInfoNr.java @@ -68,6 +68,14 @@ public final class CellInfoNr extends CellInfo { mCellSignalStrength = new CellSignalStrengthNr(cil.signalStrengthNr); } + /** @hide */ + public CellInfoNr(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_6.CellInfoNr cil = ci.ratSpecificInfo.nr(); + mCellIdentity = new CellIdentityNr(cil.cellIdentityNr); + mCellSignalStrength = new CellSignalStrengthNr(cil.signalStrengthNr); + } + /** * @return a {@link CellIdentityNr} instance. */ diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java index 038c49ac37ee..994b317a47fd 100644 --- a/telephony/java/android/telephony/CellInfoTdscdma.java +++ b/telephony/java/android/telephony/CellInfoTdscdma.java @@ -85,6 +85,14 @@ public final class CellInfoTdscdma extends CellInfo implements Parcelable { mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); } + /** @hide */ + public CellInfoTdscdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_5.CellInfoTdscdma cit = ci.ratSpecificInfo.tdscdma(); + mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma); + mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); + } + /** * @return a {@link CellIdentityTdscdma} instance. */ diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java index c74955f807b0..62ac0b8ae04e 100644 --- a/telephony/java/android/telephony/CellInfoWcdma.java +++ b/telephony/java/android/telephony/CellInfoWcdma.java @@ -80,6 +80,14 @@ public final class CellInfoWcdma extends CellInfo implements Parcelable { mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); } + /** @hide */ + public CellInfoWcdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_5.CellInfoWcdma ciw = ci.ratSpecificInfo.wcdma(); + mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma); + mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); + } + /** * @return a {@link CellIdentityWcdma} instance. */ diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index 47a8f72a2337..db7d10ae8ce4 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -86,6 +86,15 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P private int mRsrq; @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mRssnr; + /** + * CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 136.213 section 7.2.3. + * + * Range [1, 6]. + */ + private int mCqiTableIndex; @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mCqi; @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) @@ -120,24 +129,42 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P * @param rsrp in dBm [-140,-43], UNKNOWN * @param rsrq in dB [-34, 3], UNKNOWN * @param rssnr in dB [-20, +30], UNKNOWN + * @param cqiTableIndex [1, 6], UNKNOWN * @param cqi [0, 15], UNKNOWN * @param timingAdvance [0, 1282], UNKNOWN * */ /** @hide */ - public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi, - int timingAdvance) { - + public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqiTableIndex, + int cqi, int timingAdvance) { mRssi = inRangeOrUnavailable(rssi, -113, -51); mSignalStrength = mRssi; mRsrp = inRangeOrUnavailable(rsrp, -140, -43); mRsrq = inRangeOrUnavailable(rsrq, -34, 3); mRssnr = inRangeOrUnavailable(rssnr, -20, 30); + mCqiTableIndex = inRangeOrUnavailable(cqiTableIndex, 1, 6); mCqi = inRangeOrUnavailable(cqi, 0, 15); mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282); updateLevel(null, null); } + /** + * Construct a cell signal strength + * + * @param rssi in dBm [-113,-51], UNKNOWN + * @param rsrp in dBm [-140,-43], UNKNOWN + * @param rsrq in dB [-34, 3], UNKNOWN + * @param rssnr in dB [-20, +30], UNKNOWN + * @param cqi [0, 15], UNKNOWN + * @param timingAdvance [0, 1282], UNKNOWN + * + */ + /** @hide */ + public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi, + int timingAdvance) { + this(rssi, rsrp, rsrq, rssnr, CellInfo.UNAVAILABLE, cqi, timingAdvance); + } + /** @hide */ public CellSignalStrengthLte(android.hardware.radio.V1_0.LteSignalStrength lte) { // Convert from HAL values as part of construction. @@ -148,6 +175,16 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } /** @hide */ + public CellSignalStrengthLte(android.hardware.radio.V1_6.LteSignalStrength lte) { + // Convert from HAL values as part of construction. + this(convertRssiAsuToDBm(lte.base.signalStrength), + lte.base.rsrp != CellInfo.UNAVAILABLE ? -lte.base.rsrp : lte.base.rsrp, + lte.base.rsrq != CellInfo.UNAVAILABLE ? -lte.base.rsrq : lte.base.rsrq, + convertRssnrUnitFromTenDbToDB(lte.base.rssnr), lte.cqiTableIndex, lte.base.cqi, + lte.base.timingAdvance); + } + + /** @hide */ public CellSignalStrengthLte(CellSignalStrengthLte s) { copyFrom(s); } @@ -159,6 +196,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mRsrp = s.mRsrp; mRsrq = s.mRsrq; mRssnr = s.mRssnr; + mCqiTableIndex = s.mCqiTableIndex; mCqi = s.mCqi; mTimingAdvance = s.mTimingAdvance; mLevel = s.mLevel; @@ -179,6 +217,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mRsrp = CellInfo.UNAVAILABLE; mRsrq = CellInfo.UNAVAILABLE; mRssnr = CellInfo.UNAVAILABLE; + mCqiTableIndex = CellInfo.UNAVAILABLE; mCqi = CellInfo.UNAVAILABLE; mTimingAdvance = CellInfo.UNAVAILABLE; mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; @@ -402,6 +441,17 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } /** + * Get table index for channel quality indicator + * + * @return the CQI table index if available or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + /** @hide */ + public int getCqiTableIndex() { + return mCqiTableIndex; + } + + /** * Get channel quality indicator * * @return the CQI if available or @@ -454,7 +504,8 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P @Override public int hashCode() { - return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance, mLevel); + return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqiTableIndex, mCqi, mTimingAdvance, + mLevel); } private static final CellSignalStrengthLte sInvalid = new CellSignalStrengthLte(); @@ -476,6 +527,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P && mRsrp == s.mRsrp && mRsrq == s.mRsrq && mRssnr == s.mRssnr + && mCqiTableIndex == s.mCqiTableIndex && mCqi == s.mCqi && mTimingAdvance == s.mTimingAdvance && mLevel == s.mLevel; @@ -491,6 +543,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P + " rsrp=" + mRsrp + " rsrq=" + mRsrq + " rssnr=" + mRssnr + + " cqiTableIndex=" + mCqiTableIndex + " cqi=" + mCqi + " ta=" + mTimingAdvance + " level=" + mLevel @@ -508,6 +561,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P dest.writeInt(mRsrp); dest.writeInt(mRsrq); dest.writeInt(mRssnr); + dest.writeInt(mCqiTableIndex); dest.writeInt(mCqi); dest.writeInt(mTimingAdvance); dest.writeInt(mLevel); @@ -523,6 +577,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mRsrp = in.readInt(); mRsrq = in.readInt(); mRssnr = in.readInt(); + mCqiTableIndex = in.readInt(); mCqi = in.readInt(); mTimingAdvance = in.readInt(); mLevel = in.readInt(); diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index 766019ec382a..1518190bb7f7 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.IntRange; +import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -27,7 +28,10 @@ import com.android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * 5G NR signal strength related information. @@ -109,6 +113,28 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa private int mCsiRsrp; private int mCsiRsrq; private int mCsiSinr; + /** + * CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [1, 3]. + */ + private int mCsiCqiTableIndex; + /** + * CSI channel quality indicators (CQI) for all subbands. + * + * If the CQI report is for the entire wideband, a single CQI index is provided. + * If the CQI report is for all subbands, one CQI index is provided for each subband, + * in ascending order of subband index. + * If CQI is not available, the CQI report is empty. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [0, 15] for each CQI. + */ + private List<Integer> mCsiCqiReport;; private int mSsRsrp; private int mSsRsrq; private int mSsSinr; @@ -138,16 +164,22 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa * @param csiRsrp CSI reference signal received power. * @param csiRsrq CSI reference signal received quality. * @param csiSinr CSI signal-to-noise and interference ratio. + * @param csiCqiTableIndex CSI CSI channel quality indicator (CQI) table index. + * @param csiCqiReport CSI channel quality indicators (CQI) for all subbands. * @param ssRsrp SS reference signal received power. * @param ssRsrq SS reference signal received quality. * @param ssSinr SS signal-to-noise and interference ratio. * @hide */ - public CellSignalStrengthNr( - int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) { + public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex, + List<Integer> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) { mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44); mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3); mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23); + mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3); + mCsiCqiReport = csiCqiReport.stream() + .map(cqi -> new Integer(inRangeOrUnavailable(cqi.intValue(), 1, 3))) + .collect(Collectors.toList()); mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44); mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20); mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40); @@ -155,6 +187,21 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** + * @param csiRsrp CSI reference signal received power. + * @param csiRsrq CSI reference signal received quality. + * @param csiSinr CSI signal-to-noise and interference ratio. + * @param ssRsrp SS reference signal received power. + * @param ssRsrq SS reference signal received quality. + * @param ssSinr SS signal-to-noise and interference ratio. + * @hide + */ + public CellSignalStrengthNr( + int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) { + this(csiRsrp, csiRsrq, csiSinr, CellInfo.UNAVAILABLE, Collections.emptyList(), + ssRsrp, ssRsrq, ssSinr); + } + + /** * @hide * @param ss signal strength from modem. */ @@ -164,6 +211,15 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** + * @hide + * @param ss signal strength from modem. + */ + public CellSignalStrengthNr(android.hardware.radio.V1_6.NrSignalStrength ss) { + this(flip(ss.base.csiRsrp), flip(ss.base.csiRsrq), ss.base.csiSinr, ss.csiCqiTableIndex, + ss.csiCqiReport, flip(ss.base.ssRsrp), flip(ss.base.ssRsrq), ss.base.ssSinr); + } + + /** * Flip sign cell strength value when taking in the value from hal * @param val cell strength value * @return flipped value @@ -232,6 +288,36 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa return mCsiSinr; } + /** + * Return CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [1, 3]. + */ + /** @hide */ + public int getCsiCqiTableIndex() { + return mCsiCqiTableIndex; + } + /** + * Return a list of CSI channel quality indicators (CQI) for all subbands. + * + * If the CQI report is for the entire wideband, a single CQI index is provided. + * If the CQI report is for all subbands, one CQI index is provided for each subband, + * in ascending order of subband index. + * If CQI is not available, the CQI report is empty. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [0, 15] for each CQI. + */ + /** @hide */ + @NonNull + public List<Integer> getCsiCqiReport() { + return mCsiCqiReport; + } + @Override public int describeContents() { return 0; @@ -243,6 +329,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa dest.writeInt(mCsiRsrp); dest.writeInt(mCsiRsrq); dest.writeInt(mCsiSinr); + dest.writeInt(mCsiCqiTableIndex); + dest.writeList(mCsiCqiReport); dest.writeInt(mSsRsrp); dest.writeInt(mSsRsrq); dest.writeInt(mSsSinr); @@ -253,6 +341,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mCsiRsrp = in.readInt(); mCsiRsrq = in.readInt(); mCsiSinr = in.readInt(); + mCsiCqiTableIndex = in.readInt(); + mCsiCqiReport = in.readArrayList(Integer.class.getClassLoader()); mSsRsrp = in.readInt(); mSsRsrq = in.readInt(); mSsSinr = in.readInt(); @@ -265,6 +355,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mCsiRsrp = CellInfo.UNAVAILABLE; mCsiRsrq = CellInfo.UNAVAILABLE; mCsiSinr = CellInfo.UNAVAILABLE; + mCsiCqiTableIndex = CellInfo.UNAVAILABLE; + mCsiCqiReport = Collections.emptyList(); mSsRsrp = CellInfo.UNAVAILABLE; mSsRsrq = CellInfo.UNAVAILABLE; mSsSinr = CellInfo.UNAVAILABLE; @@ -408,6 +500,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mCsiRsrp = s.mCsiRsrp; mCsiRsrq = s.mCsiRsrq; mCsiSinr = s.mCsiSinr; + mCsiCqiTableIndex = s.mCsiCqiTableIndex; + mCsiCqiReport = s.mCsiCqiReport; mSsRsrp = s.mSsRsrp; mSsRsrq = s.mSsRsrq; mSsSinr = s.mSsSinr; @@ -423,7 +517,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa @Override public int hashCode() { - return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr, mLevel); + return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mCsiCqiTableIndex, + mCsiCqiReport, mSsRsrp, mSsRsrq, mSsSinr, mLevel); } private static final CellSignalStrengthNr sInvalid = new CellSignalStrengthNr(); @@ -439,6 +534,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa if (obj instanceof CellSignalStrengthNr) { CellSignalStrengthNr o = (CellSignalStrengthNr) obj; return mCsiRsrp == o.mCsiRsrp && mCsiRsrq == o.mCsiRsrq && mCsiSinr == o.mCsiSinr + && mCsiCqiTableIndex == o.mCsiCqiTableIndex + && mCsiCqiReport.equals(o.mCsiCqiReport) && mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr && mLevel == o.mLevel; } @@ -451,7 +548,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa .append(TAG + ":{") .append(" csiRsrp = " + mCsiRsrp) .append(" csiRsrq = " + mCsiRsrq) - .append(" csiSinr = " + mCsiSinr) + .append(" csiCqiTableIndex = " + mCsiCqiTableIndex) + .append(" csiCqiReport = " + mCsiCqiReport) .append(" ssRsrp = " + mSsRsrp) .append(" ssRsrq = " + mSsRsrq) .append(" ssSinr = " + mSsSinr) diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java index b785037e51c1..6571858fc4ae 100644 --- a/telephony/java/android/telephony/PhoneCapability.java +++ b/telephony/java/android/telephony/PhoneCapability.java @@ -28,8 +28,6 @@ import java.util.Objects; /** * Define capability of a modem group. That is, the capabilities * are shared between those modems defined by list of modem IDs. - * - * @hide */ public final class PhoneCapability implements Parcelable { // Hardcoded default DSDS capability. diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index 8d49e15da934..95c69ba470c4 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -29,10 +29,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; -/** - * @hide - */ -@SystemApi public final class PhysicalChannelConfig implements Parcelable { // TODO(b/72993578) consolidate these enums in a central location. @@ -86,7 +82,7 @@ public final class PhysicalChannelConfig implements Parcelable { private int mFrequencyRange; /** - * The absolute radio frequency channel number, {@link CHANNEL_NUMBER_UNKNOWN} if unknown. + * The absolute radio frequency channel number, {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. */ private int mChannelNumber; @@ -97,7 +93,7 @@ public final class PhysicalChannelConfig implements Parcelable { private int[] mContextIds; /** - * The physical cell identifier for this cell - PCI, PSC, {@link PHYSICAL_CELL_ID_UNKNOWN} + * The physical cell identifier for this cell - PCI, PSC, {@link #PHYSICAL_CELL_ID_UNKNOWN} * if unknown. */ private int mPhysicalCellId; @@ -153,7 +149,7 @@ public final class PhysicalChannelConfig implements Parcelable { /** * @return the absolute radio frequency channel number for this physical channel, - * {@link CHANNEL_NUMBER_UNKNOWN} if unknown. + * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. */ public int getChannelNumber() { return mChannelNumber; @@ -169,7 +165,7 @@ public final class PhysicalChannelConfig implements Parcelable { * In 5G RAN, this value is physical layer cell identity. The range is [0, 1007]. * Reference: 3GPP TS 38.211 section 7.4.2.1. * - * @return the physical cell identifier for this cell, {@link PHYSICAL_CELL_ID_UNKNOWN} + * @return the physical cell identifier for this cell, {@link #PHYSICAL_CELL_ID_UNKNOWN} * if {@link android.telephony.CellInfo#UNAVAILABLE}. */ @IntRange(from = 0, to = 1007) diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index 7bd0bc0b69ce..b317c5557108 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -187,6 +187,21 @@ public class SignalStrength implements Parcelable { new CellSignalStrengthNr(signalStrength.nr)); } + /** + * Constructor for Radio HAL V1.6. + * + * @param signalStrength signal strength reported from modem. + * @hide + */ + public SignalStrength(android.hardware.radio.V1_6.SignalStrength signalStrength) { + this(new CellSignalStrengthCdma(signalStrength.cdma, signalStrength.evdo), + new CellSignalStrengthGsm(signalStrength.gsm), + new CellSignalStrengthWcdma(signalStrength.wcdma), + new CellSignalStrengthTdscdma(signalStrength.tdscdma), + new CellSignalStrengthLte(signalStrength.lte), + new CellSignalStrengthNr(signalStrength.nr)); + } + private CellSignalStrength getPrimary() { // This behavior is intended to replicate the legacy behavior of getLevel() by prioritizing // newer faster RATs for default/for display purposes. diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index d963970a3d2b..239329cb447e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -124,6 +124,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -155,6 +156,7 @@ import java.util.function.Consumer; public class TelephonyManager { private static final String TAG = "TelephonyManager"; + private TelephonyRegistryManager mTelephonyRegistryMgr; /** * To expand the error codes for {@link TelephonyManager#updateAvailableNetworks} and * {@link TelephonyManager#setPreferredOpportunisticDataSubscription}. @@ -5574,12 +5576,22 @@ public class TelephonyManager { * @param events The telephony state(s) of interest to the listener, * as a bitwise-OR combination of {@link PhoneStateListener} * LISTEN_ flags. - * @deprecated use {@link #listen(long, PhoneStateListener) instead due to the event number - * limit increased to 64. + * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}. */ @Deprecated public void listen(PhoneStateListener listener, int events) { - listen(events, listener); + boolean notifyNow = getITelephony() != null; + mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); + if (mTelephonyRegistryMgr != null) { + if (events != PhoneStateListener.LISTEN_NONE) { + mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(mSubId, + getOpPackageName(), getAttributionTag(), listener, events, notifyNow); + } else { + unregisterPhoneStateListener(listener); + } + } else { + throw new IllegalStateException("telephony service is null."); + } } /** @@ -5615,18 +5627,21 @@ public class TelephonyManager { * LISTEN_ flags. * @param listener The {@link PhoneStateListener} object to register * (or unregister) + * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}. */ + @Deprecated public void listen(long events, @NonNull PhoneStateListener listener) { - if (mContext == null) return; - boolean notifyNow = (getITelephony() != null); - TelephonyRegistryManager telephonyRegistry = - (TelephonyRegistryManager) - mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); - if (telephonyRegistry != null) { - telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getAttributionTag(), - listener, events, notifyNow); + mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); + if (mTelephonyRegistryMgr != null) { + if (events != PhoneStateListener.LISTEN_NONE) { + mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(mSubId, + getOpPackageName(), getAttributionTag(), listener, + Long.valueOf(events).intValue(), getITelephony() != null); + } else { + unregisterPhoneStateListener(listener); + } } else { - Rlog.w(TAG, "telephony registry not ready."); + throw new IllegalStateException("telephony service is null."); } } @@ -11694,18 +11709,6 @@ public class TelephonyManager { } /** - * In this mode, modem will not send specified indications when screen is off. - * @hide - */ - public static final int INDICATION_UPDATE_MODE_NORMAL = 1; - - /** - * In this mode, modem will still send specified indications when screen is off. - * @hide - */ - public static final int INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF = 2; - - /** * The indication for signal strength update. * @hide */ @@ -12302,23 +12305,15 @@ public class TelephonyManager { @NonNull public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList( @EmergencyServiceCategories int categories) { - Map<Integer, List<EmergencyNumber>> emergencyNumberList = new HashMap<>(); + Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>(); try { ITelephony telephony = getITelephony(); if (telephony != null) { - emergencyNumberList = telephony.getEmergencyNumberList( - mContext.getOpPackageName(), mContext.getAttributionTag()); - if (emergencyNumberList != null) { - for (Integer subscriptionId : emergencyNumberList.keySet()) { - List<EmergencyNumber> numberList = emergencyNumberList.get(subscriptionId); - for (EmergencyNumber number : numberList) { - if (!number.isInEmergencyServiceCategories(categories)) { - numberList.remove(number); - } - } - } - } - return emergencyNumberList; + Map<Integer, List<EmergencyNumber>> emergencyNumberList = + telephony.getEmergencyNumberList(mContext.getOpPackageName(), + mContext.getAttributionTag()); + emergencyNumberListForCategories = + filterEmergencyNumbersByCategories(emergencyNumberList, categories); } else { throw new IllegalStateException("telephony service is null."); } @@ -12326,7 +12321,34 @@ public class TelephonyManager { Log.e(TAG, "getEmergencyNumberList with Categories RemoteException", ex); ex.rethrowAsRuntimeException(); } - return emergencyNumberList; + return emergencyNumberListForCategories; + } + + /** + * Filter emergency numbers with categories. + * + * @hide + */ + @VisibleForTesting + public Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories( + Map<Integer, List<EmergencyNumber>> emergencyNumberList, + @EmergencyServiceCategories int categories) { + Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>(); + if (emergencyNumberList != null) { + for (Integer subscriptionId : emergencyNumberList.keySet()) { + List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get( + subscriptionId); + List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>(); + for (EmergencyNumber number : allNumbersForSub) { + if (number.isInEmergencyServiceCategories(categories)) { + numbersForCategoriesPerSub.add(number); + } + } + emergencyNumberListForCategories.put( + subscriptionId, numbersForCategoriesPerSub); + } + } + return emergencyNumberListForCategories; } /** @@ -14328,4 +14350,69 @@ public class TelephonyManager { } return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR; } + /** + * Registers a listener object to receive notification of changes + * in specified telephony states. + * <p> + * To register a listener, pass a {@link PhoneStateListener} which implements + * interfaces of events. For example, + * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements + * {@link PhoneStateListener.ServiceStateChangedListener}. + * + * At registration, and when a specified telephony state changes, the telephony manager invokes + * the appropriate callback method on the listener object and passes the current (updated) + * values. + * <p> + * + * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, + * applies to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds, + * pass a separate listener object to each TelephonyManager object created with + * {@link #createForSubscriptionId}. + * + * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> + * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A + * {@link SecurityException} will be thrown otherwise. + * + * This API should be used sparingly -- large numbers of listeners will cause system + * instability. If a process has registered too many listeners without unregistering them, it + * may encounter an {@link IllegalStateException} when trying to register more listeners. + * + * @param executor The executor of where the callback will execute. + * @param listener The {@link PhoneStateListener} object to register. + */ + public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, + @NonNull PhoneStateListener listener) { + if (executor == null || listener == null) { + throw new IllegalArgumentException("PhoneStateListener and executor must be non-null"); + } + mTelephonyRegistryMgr = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (mTelephonyRegistryMgr != null) { + mTelephonyRegistryMgr.registerPhoneStateListener(executor, mSubId, + getOpPackageName(), getAttributionTag(), listener, getITelephony() != null); + } else { + throw new IllegalStateException("telephony service is null."); + } + } + + /** + * Unregister an existing {@link PhoneStateListener}. + * + * @param listener The {@link PhoneStateListener} object to unregister. + */ + public void unregisterPhoneStateListener(@NonNull PhoneStateListener listener) { + + if (mContext == null) { + throw new IllegalStateException("telephony service is null."); + } + + mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); + if (mTelephonyRegistryMgr != null) { + mTelephonyRegistryMgr.unregisterPhoneStateListener(mSubId, getOpPackageName(), + getAttributionTag(), listener, getITelephony() != null); + } else { + throw new IllegalStateException("telephony service is null."); + } + } } diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index e8234178be9d..9a3f592480d1 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -231,8 +231,9 @@ public class MmTelFeature extends ImsFeature { * The capabilities that are used in MmTelFeature are defined as * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE}, * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and - * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}. + * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, + * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}, and + * {@link MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER}. * * The capabilities of this MmTelFeature will be set by the framework. */ @@ -275,7 +276,8 @@ public class MmTelFeature extends ImsFeature { CAPABILITY_TYPE_VOICE, CAPABILITY_TYPE_VIDEO, CAPABILITY_TYPE_UT, - CAPABILITY_TYPE_SMS + CAPABILITY_TYPE_SMS, + CAPABILITY_TYPE_CALL_COMPOSER }) @Retention(RetentionPolicy.SOURCE) public @interface MmTelCapability {} @@ -301,6 +303,11 @@ public class MmTelFeature extends ImsFeature { public static final int CAPABILITY_TYPE_SMS = 1 << 3; /** + * This MmTelFeature supports Call Composer (section 2.4 of RC.20) + */ + public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4; + + /** * @hide */ @Override @@ -343,6 +350,8 @@ public class MmTelFeature extends ImsFeature { builder.append(isCapable(CAPABILITY_TYPE_UT)); builder.append(" SMS: "); builder.append(isCapable(CAPABILITY_TYPE_SMS)); + builder.append(" CALL_COMPOSER: "); + builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER)); builder.append("]"); return builder.toString(); } diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index e0290a5dc2af..e757d9f70ccc 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -126,7 +126,7 @@ public class ImsConfigImplBase { */ @Override public synchronized String getConfigString(int item) throws RemoteException { - if (mProvisionedIntValue.containsKey(item)) { + if (mProvisionedStringValue.containsKey(item)) { return mProvisionedStringValue.get(item); } else { String retVal = getImsConfigImpl().getConfigString(item); diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java new file mode 100644 index 000000000000..54d70478f762 --- /dev/null +++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.os.BatteryStatsManager; +import android.os.BatteryUsageStats; +import android.os.UidBatteryConsumer; +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class BatteryUsageStatsPerfTest { + + @Rule + public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + /** + * Measures the performance of {@link BatteryStatsManager#getBatteryUsageStats()}, + * which triggers a battery stats sync on every iteration. + */ + @Test + public void testGetBatteryUsageStats() { + final Context context = InstrumentationRegistry.getContext(); + final BatteryStatsManager batteryStatsManager = + context.getSystemService(BatteryStatsManager.class); + + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats(); + + state.pauseTiming(); + + List<UidBatteryConsumer> uidBatteryConsumers = + batteryUsageStats.getUidBatteryConsumers(); + double power = 0; + for (int i = 0; i < uidBatteryConsumers.size(); i++) { + UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(i); + power += uidBatteryConsumer.getConsumedPower(); + } + + assertThat(power).isGreaterThan(0.0); + + state.resumeTiming(); + } + } +} diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml index 58f56f29528c..f9f9d5828171 100644 --- a/tests/FlickerTests/AndroidManifest.xml +++ b/tests/FlickerTests/AndroidManifest.xml @@ -21,6 +21,8 @@ <!-- Read and write traces from external storage --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <!-- Write secure settings --> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <!-- Capture screen contents --> @@ -34,7 +36,8 @@ <!-- Workaround grant runtime permission exception from b/152733071 --> <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> <uses-permission android:name="android.permission.READ_LOGS"/> - <application> + <!-- Allow the test to write directly to /sdcard/ --> + <application android:requestLegacyExternalStorage="true"> <uses-library android:name="android.test.runner"/> </application> diff --git a/tests/FlickerTests/AndroidTestPhysicalDevices.xml b/tests/FlickerTests/AndroidTestPhysicalDevices.xml index 16504389098c..abd620f5c157 100644 --- a/tests/FlickerTests/AndroidTestPhysicalDevices.xml +++ b/tests/FlickerTests/AndroidTestPhysicalDevices.xml @@ -34,7 +34,7 @@ <option name="hidden-api-checks" value="false" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> - <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.server.wm.flicker/files" /> + <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> <option name="clean-up" value="true" /> </metrics_collector> diff --git a/tests/FlickerTests/AndroidTestVirtualDevices.xml b/tests/FlickerTests/AndroidTestVirtualDevices.xml index 222212a74cd0..9a5413ae3ad2 100644 --- a/tests/FlickerTests/AndroidTestVirtualDevices.xml +++ b/tests/FlickerTests/AndroidTestVirtualDevices.xml @@ -34,7 +34,7 @@ <option name="hidden-api-checks" value="false" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> - <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.server.wm.flicker/files" /> + <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> <option name="clean-up" value="true" /> </metrics_collector> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt index a85e92c92093..bb03237ca4c6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -239,7 +239,7 @@ fun EventLogAssertion.focusChanges( bugId: Int = 0, enabled: Boolean = bugId == 0 ) { - all(enabled = enabled, bugId = bugId) { + all("focusChanges", bugId, enabled) { this.focusChanges(windows) } } @@ -248,7 +248,7 @@ fun EventLogAssertion.focusDoesNotChange( bugId: Int = 0, enabled: Boolean = bugId == 0 ) { - all(enabled = enabled, bugId = bugId) { + all("focusDoesNotChange", bugId, enabled) { this.focusDoesNotChange() } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index 0c584f4973e8..5aef314e0caf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -16,6 +16,7 @@ package com.android.server.wm.flicker.launch +import androidx.test.filters.FlakyTest import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -53,6 +54,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 174658929) class OpenAppFromOverviewTest( testName: String, flickerSpec: Flicker diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index 0db064ab79c5..9d4a71874f67 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -67,7 +67,7 @@ class ChangeAppRotationTest( val instrumentation = InstrumentationRegistry.getInstrumentation() val testApp = StandardAppHelper(instrumentation, "com.android.server.wm.flicker.testapp", "SimpleApp") - return FlickerTestRunnerFactory(instrumentation) + return FlickerTestRunnerFactory(instrumentation, repetitions = 10) .buildRotationTest { configuration -> withTestName { buildTestTag( diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index a8aab2a899fb..a72b07c45dd8 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -6,6 +6,7 @@ android_test { static_libs: [ "androidx.test.ext.junit", "androidx.test.rules", + "truth-prebuilt", "ub-uiautomator", ], test_suites: ["device-tests"], diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt new file mode 100644 index 000000000000..f919a3eaf271 --- /dev/null +++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt @@ -0,0 +1,78 @@ +/* + * 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.test.input + +import android.graphics.FrameInfo +import android.os.SystemClock +import android.view.ViewFrameInfo +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test + +class ViewFrameInfoTest { + companion object { + private const val TAG = "ViewFrameInfoTest" + } + private val mViewFrameInfo = ViewFrameInfo() + private var mTimeStarted: Long = 0 + + @Before + fun setUp() { + mViewFrameInfo.reset() + mViewFrameInfo.updateOldestInputEvent(10) + mViewFrameInfo.updateNewestInputEvent(20) + mViewFrameInfo.flags = mViewFrameInfo.flags or FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED + mTimeStarted = SystemClock.uptimeNanos() + mViewFrameInfo.markDrawStart() + } + + @Test + fun testPopulateFields() { + assertThat(mViewFrameInfo.drawStart).isGreaterThan(mTimeStarted) + assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(10) + assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(20) + assertThat(mViewFrameInfo.flags).isEqualTo(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED) + } + + @Test + fun testReset() { + mViewFrameInfo.reset() + // Ensure that the original object is reset correctly + assertThat(mViewFrameInfo.drawStart).isEqualTo(0) + assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(0) + assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(0) + assertThat(mViewFrameInfo.flags).isEqualTo(0) + } + + @Test + fun testUpdateFrameInfoFromViewFrameInfo() { + val frameInfo = FrameInfo() + // By default, all values should be zero + assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(0) + assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(0) + assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(0) + assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isEqualTo(0) + + // The values inside FrameInfo should match those from ViewFrameInfo after we update them + mViewFrameInfo.populateFrameInfo(frameInfo) + assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(10) + assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(20) + assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo( + FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED) + assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted) + } +}
\ No newline at end of file diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 9738e58543e1..104758de49f1 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -22,6 +22,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -43,10 +44,15 @@ import android.os.SystemProperties; import android.os.test.TestLooper; import android.provider.DeviceConfig; import android.util.AtomicFile; +import android.util.LongArrayQueue; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import android.util.Xml; import androidx.test.InstrumentationRegistry; import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.internal.util.XmlUtils; import com.android.server.PackageWatchdog.HealthCheckState; import com.android.server.PackageWatchdog.MonitoredPackage; import com.android.server.PackageWatchdog.PackageHealthObserver; @@ -64,6 +70,7 @@ import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.io.File; +import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -739,7 +746,8 @@ public class PackageWatchdogTest { false /* hasPassedHealthCheck */); MonitoredPackage m2 = wd.newMonitoredPackage(APP_B, LONG_DURATION, false); MonitoredPackage m3 = wd.newMonitoredPackage(APP_C, LONG_DURATION, false); - MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true); + MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true, + new LongArrayQueue()); // Verify transition: inactive -> active -> passed // Verify initially inactive @@ -1210,6 +1218,73 @@ public class PackageWatchdogTest { assertThat(observer.mMitigationCounts).isEqualTo(List.of(1, 2, 3, 3, 2, 3)); } + @Test + public void testNormalizingMitigationCalls() { + PackageWatchdog watchdog = createWatchdog(); + + LongArrayQueue mitigationCalls = new LongArrayQueue(); + mitigationCalls.addLast(1000); + mitigationCalls.addLast(2000); + mitigationCalls.addLast(3000); + + MonitoredPackage pkg = watchdog.newMonitoredPackage( + "test", 123, 456, true, mitigationCalls); + + // Make current system uptime 10000ms. + moveTimeForwardAndDispatch(9999); + + LongArrayQueue expectedCalls = pkg.normalizeMitigationCalls(); + + assertThat(expectedCalls.size()).isEqualTo(mitigationCalls.size()); + + for (int i = 0; i < mitigationCalls.size(); i++) { + assertThat(expectedCalls.get(i)).isEqualTo(mitigationCalls.get(i) - 10000); + } + } + + /** + * Ensure that a {@link MonitoredPackage} may be correctly written and read in order to persist + * across reboots. + */ + @Test + public void testWritingAndReadingMonitoredPackage() throws Exception { + PackageWatchdog watchdog = createWatchdog(); + + LongArrayQueue mitigationCalls = new LongArrayQueue(); + mitigationCalls.addLast(1000); + mitigationCalls.addLast(2000); + mitigationCalls.addLast(3000); + MonitoredPackage writePkg = watchdog.newMonitoredPackage( + "test.package", 1000, 2000, true, mitigationCalls); + + // Move time forward so that the current uptime is 4000ms. Therefore, the written mitigation + // calls will each be reduced by 4000. + moveTimeForwardAndDispatch(3999); + LongArrayQueue expectedCalls = new LongArrayQueue(); + expectedCalls.addLast(-3000); + expectedCalls.addLast(-2000); + expectedCalls.addLast(-1000); + MonitoredPackage expectedPkg = watchdog.newMonitoredPackage( + "test.package", 1000, 2000, true, expectedCalls); + + // Write the package + File tmpFile = File.createTempFile("package-watchdog-test", ".xml"); + AtomicFile testFile = new AtomicFile(tmpFile); + FileOutputStream stream = testFile.startWrite(); + TypedXmlSerializer outputSerializer = Xml.resolveSerializer(stream); + outputSerializer.startDocument(null, true); + writePkg.writeLocked(outputSerializer); + outputSerializer.endDocument(); + testFile.finishWrite(stream); + + // Read the package + TypedXmlPullParser parser = Xml.resolvePullParser(testFile.openRead()); + XmlUtils.beginDocument(parser, "package"); + MonitoredPackage readPkg = watchdog.parseMonitoredPackage(parser); + + assertTrue(readPkg.isEqualTo(expectedPkg)); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING index 0f4c4603f9b4..7f9f2dcf2bde 100644 --- a/tests/RollbackTest/TEST_MAPPING +++ b/tests/RollbackTest/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit": [ + "presubmit-large": [ { "name": "RollbackTest" }, diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index f55d4d474bfb..3a40696d1add 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -31,7 +31,6 @@ java_test_host { "testng", "compatibility-tradefed", "frameworks-base-hostutils", - "module_test_util", "cts-install-lib-host", ], data: [ diff --git a/tests/StagedInstallTest/TEST_MAPPING b/tests/StagedInstallTest/TEST_MAPPING index 5a7a5a766b88..fa2a60b21b50 100644 --- a/tests/StagedInstallTest/TEST_MAPPING +++ b/tests/StagedInstallTest/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit": [ + "presubmit-large": [ { "name": "StagedInstallInternalTest" } diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index 27ccbc78cf96..9e1ea2e04528 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -29,7 +29,6 @@ import android.cts.install.lib.host.InstallUtilsHost; import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; import com.android.ddmlib.Log; import com.android.tests.rollback.host.AbandonSessionsRule; -import com.android.tests.util.ModuleTestUtils; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -60,7 +59,6 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { private static final String APK_A = "TestAppAv1.apk"; private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; - private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this); private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); /** @@ -161,7 +159,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - final File apexFile = mTestUtils.getTestFile(SHIM_V2); + final File apexFile = mHostUtils.getTestFile(SHIM_V2); final String output = getDevice().executeAdbCommand("install", "--staged", "--staged-ready-timeout", "60000", apexFile.getAbsolutePath()); assertThat(output).contains("Reboot device to apply staged session"); @@ -176,7 +174,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - final File apexFile = mTestUtils.getTestFile(SHIM_V2); + final File apexFile = mHostUtils.getTestFile(SHIM_V2); final String output = getDevice().executeAdbCommand("install", "--staged", apexFile.getAbsolutePath()); assertThat(output).contains("Reboot device to apply staged session"); @@ -191,7 +189,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - final File apexFile = mTestUtils.getTestFile(SHIM_V2); + final File apexFile = mHostUtils.getTestFile(SHIM_V2); final String output = getDevice().executeAdbCommand("install", "--staged", "--staged-ready-timeout", "0", apexFile.getAbsolutePath()); assertThat(output).doesNotContain("Reboot device to apply staged session"); @@ -207,7 +205,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - final File apexFile = mTestUtils.getTestFile(SHIM_V2); + final File apexFile = mHostUtils.getTestFile(SHIM_V2); String output = getDevice().executeAdbCommand("install", "--staged", "--enable-rollback", apexFile.getAbsolutePath()); assertThat(output).contains("Reboot device to apply staged session"); @@ -224,8 +222,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - final File apexFile = mTestUtils.getTestFile(SHIM_V2); - final File apkFile = mTestUtils.getTestFile(APK_A); + final File apexFile = mHostUtils.getTestFile(SHIM_V2); + final File apkFile = mHostUtils.getTestFile(APK_A); final String output = getDevice().executeAdbCommand("install-multi-package", apexFile.getAbsolutePath(), apkFile.getAbsolutePath()); assertThat(output).contains("Created parent session"); diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt index cd4b38516bc3..273833b91950 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt @@ -23,7 +23,7 @@ import android.util.Log import androidx.test.ext.junit.rules.ActivityScenarioRule import com.android.server.wm.flicker.monitor.LayersTraceMonitor import com.android.server.wm.flicker.monitor.withSFTracing -import com.android.server.wm.flicker.traces.layers.LayersTrace +import com.android.server.wm.traces.parser.layers.LayersTrace import junit.framework.Assert import org.junit.After import org.junit.Before @@ -52,7 +52,8 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) : } fun withTrace(predicate: (it: MainActivity) -> Unit): LayersTrace { - return withSFTracing(instrumentation, TRACE_FLAGS) { + return withSFTracing(TRACE_FLAGS, + outputDir = instrumentation.targetContext.dataDir.toPath()) { scenarioRule.getScenario().onActivity { predicate(it) } diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt index fe9deae80407..2e3467aff9ba 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt @@ -68,7 +68,8 @@ class ResizeTasksSyncTest { val firstBounds = Rect(0, 0, 1080, 800) val secondBounds = Rect(0, 1000, 1080, 1800) - val trace = withSFTracing(instrumentation, TRACE_FLAGS) { + val trace = withSFTracing(TRACE_FLAGS, + outputDir = instrumentation.targetContext.dataDir.toPath()) { lateinit var resizeReadyLatch: CountDownLatch scenarioRule.getScenario().onActivity { resizeReadyLatch = it.resizeTaskView(firstBounds, secondBounds) @@ -77,17 +78,13 @@ class ResizeTasksSyncTest { } // find the frame which match resized buffer size. - var frame: Long = -1 - loop@ for (trace in trace.entries) { - for (layer in trace.flattenedLayers) { - if (layer.proto.activeBuffer != null && - layer.proto.activeBuffer.width == firstBounds.width() && - layer.proto.activeBuffer.height == firstBounds.height()) { - frame = layer.proto.currFrame - break@loop - } - } - } + val frame = trace.entries.flatMap { it.flattenedLayers } + .firstOrNull { layer -> + !layer.isActiveBufferEmpty && + layer.activeBuffer?.width == firstBounds.width() && + layer.activeBuffer?.height == firstBounds.height() + }?.currFrame ?: -1 + assertNotEquals(-1, frame) // layer bounds should be related to parent surfaceview. secondBounds.offsetTo(0, 0) diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt index dd3f5bebdb8e..77e9f12c7152 100644 --- a/tests/net/common/java/android/net/NetworkProviderTest.kt +++ b/tests/net/common/java/android/net/NetworkProviderTest.kt @@ -33,6 +33,9 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.verifyNoMoreInteractions import java.util.UUID import kotlin.test.assertEquals import kotlin.test.assertNotEquals @@ -87,8 +90,8 @@ class NetworkProviderTest { ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) } } - private fun createNetworkProvider(): TestNetworkProvider { - return TestNetworkProvider(context, mHandlerThread.looper) + private fun createNetworkProvider(ctx: Context = context): TestNetworkProvider { + return TestNetworkProvider(ctx, mHandlerThread.looper) } @Test @@ -169,7 +172,12 @@ class NetworkProviderTest { @Test fun testDeclareNetworkRequestUnfulfillable() { - val provider = createNetworkProvider() + val mockContext = mock(Context::class.java) + val provider = createNetworkProvider(mockContext) + // ConnectivityManager not required at creation time + verifyNoMoreInteractions(mockContext) + doReturn(mCm).`when`(mockContext).getSystemService(Context.CONNECTIVITY_SERVICE) + mCm.registerNetworkProvider(provider) val specifier = StringNetworkSpecifier(UUID.randomUUID().toString()) diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/tests/net/java/com/android/server/NetworkManagementServiceTest.java index 968b3071bf1d..ea763d2e931e 100644 --- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java +++ b/tests/net/java/com/android/server/NetworkManagementServiceTest.java @@ -16,6 +16,12 @@ package com.android.server; +import static android.util.DebugUtils.valueToString; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -29,15 +35,19 @@ import android.content.Context; import android.net.INetd; import android.net.INetdUnsolicitedEventListener; import android.net.LinkAddress; +import android.net.NetworkPolicyManager; import android.os.BatteryStats; import android.os.Binder; import android.os.IBinder; +import android.os.Process; +import android.os.RemoteException; import android.test.suitebuilder.annotation.SmallTest; +import android.util.ArrayMap; import androidx.test.runner.AndroidJUnit4; import com.android.internal.app.IBatteryStats; -import com.android.server.NetworkManagementService.SystemServices; +import com.android.server.NetworkManagementService.Dependencies; import com.android.server.net.BaseNetworkObserver; import org.junit.After; @@ -49,13 +59,14 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.function.BiFunction; + /** * Tests for {@link NetworkManagementService}. */ @RunWith(AndroidJUnit4.class) @SmallTest public class NetworkManagementServiceTest { - private NetworkManagementService mNMService; @Mock private Context mContext; @@ -66,7 +77,9 @@ public class NetworkManagementServiceTest { @Captor private ArgumentCaptor<INetdUnsolicitedEventListener> mUnsolListenerCaptor; - private final SystemServices mServices = new SystemServices() { + private final MockDependencies mDeps = new MockDependencies(); + + private final class MockDependencies extends Dependencies { @Override public IBinder getService(String name) { switch (name) { @@ -76,14 +89,21 @@ public class NetworkManagementServiceTest { throw new UnsupportedOperationException("Unknown service " + name); } } + @Override public void registerLocalService(NetworkManagementInternal nmi) { } + @Override public INetd getNetd() { return mNetdService; } - }; + + @Override + public int getCallingUid() { + return Process.SYSTEM_UID; + } + } @Before public void setUp() throws Exception { @@ -91,7 +111,7 @@ public class NetworkManagementServiceTest { doNothing().when(mNetdService) .registerUnsolicitedEventListener(mUnsolListenerCaptor.capture()); // Start the service and wait until it connects to our socket. - mNMService = NetworkManagementService.create(mContext, mServices); + mNMService = NetworkManagementService.create(mContext, mDeps); } @After @@ -192,4 +212,105 @@ public class NetworkManagementServiceTest { // Make sure nothing else was called. verifyNoMoreInteractions(observer); } + + @Test + public void testFirewallEnabled() { + mNMService.setFirewallEnabled(true); + assertTrue(mNMService.isFirewallEnabled()); + + mNMService.setFirewallEnabled(false); + assertFalse(mNMService.isFirewallEnabled()); + } + + private static final int TEST_UID = 111; + + @Test + public void testNetworkRestrictedDefault() { + assertFalse(mNMService.isNetworkRestricted(TEST_UID)); + } + + @Test + public void testMeteredNetworkRestrictions() throws RemoteException { + // Make sure the mocked netd method returns true. + doReturn(true).when(mNetdService).bandwidthEnableDataSaver(anyBoolean()); + + // Restrict usage of mobile data in background + mNMService.setUidMeteredNetworkDenylist(TEST_UID, true); + assertTrue("Should be true since mobile data usage is restricted", + mNMService.isNetworkRestricted(TEST_UID)); + + mNMService.setDataSaverModeEnabled(true); + verify(mNetdService).bandwidthEnableDataSaver(true); + + mNMService.setUidMeteredNetworkDenylist(TEST_UID, false); + assertTrue("Should be true since data saver is on and the uid is not allowlisted", + mNMService.isNetworkRestricted(TEST_UID)); + + mNMService.setUidMeteredNetworkAllowlist(TEST_UID, true); + assertFalse("Should be false since data saver is on and the uid is allowlisted", + mNMService.isNetworkRestricted(TEST_UID)); + + // remove uid from allowlist and turn datasaver off again + mNMService.setUidMeteredNetworkAllowlist(TEST_UID, false); + mNMService.setDataSaverModeEnabled(false); + verify(mNetdService).bandwidthEnableDataSaver(false); + assertFalse("Network should not be restricted when data saver is off", + mNMService.isNetworkRestricted(TEST_UID)); + } + + @Test + public void testFirewallChains() { + final ArrayMap<Integer, ArrayMap<Integer, Boolean>> expected = new ArrayMap<>(); + // Dozable chain + final ArrayMap<Integer, Boolean> isRestrictedForDozable = new ArrayMap<>(); + isRestrictedForDozable.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); + isRestrictedForDozable.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedForDozable.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(INetd.FIREWALL_CHAIN_DOZABLE, isRestrictedForDozable); + // Powersaver chain + final ArrayMap<Integer, Boolean> isRestrictedForPowerSave = new ArrayMap<>(); + isRestrictedForPowerSave.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); + isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(INetd.FIREWALL_CHAIN_POWERSAVE, isRestrictedForPowerSave); + // Standby chain + final ArrayMap<Integer, Boolean> isRestrictedForStandby = new ArrayMap<>(); + isRestrictedForStandby.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, false); + isRestrictedForStandby.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedForStandby.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(INetd.FIREWALL_CHAIN_STANDBY, isRestrictedForStandby); + // Restricted mode chain + final ArrayMap<Integer, Boolean> isRestrictedForRestrictedMode = new ArrayMap<>(); + isRestrictedForRestrictedMode.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); + isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(INetd.FIREWALL_CHAIN_RESTRICTED, isRestrictedForRestrictedMode); + + final int[] chains = { + INetd.FIREWALL_CHAIN_STANDBY, + INetd.FIREWALL_CHAIN_POWERSAVE, + INetd.FIREWALL_CHAIN_DOZABLE, + INetd.FIREWALL_CHAIN_RESTRICTED + }; + final int[] states = { + INetd.FIREWALL_RULE_ALLOW, + INetd.FIREWALL_RULE_DENY, + NetworkPolicyManager.FIREWALL_RULE_DEFAULT + }; + BiFunction<Integer, Integer, String> errorMsg = (chain, state) -> { + return String.format("Unexpected value for chain: %s and state: %s", + valueToString(INetd.class, "FIREWALL_CHAIN_", chain), + valueToString(INetd.class, "FIREWALL_RULE_", state)); + }; + for (int chain : chains) { + final ArrayMap<Integer, Boolean> expectedValues = expected.get(chain); + mNMService.setFirewallChainEnabled(chain, true); + for (int state : states) { + mNMService.setFirewallUidRule(chain, TEST_UID, state); + assertEquals(errorMsg.apply(chain, state), + expectedValues.get(state), mNMService.isNetworkRestricted(TEST_UID)); + } + mNMService.setFirewallChainEnabled(chain, false); + } + } } diff --git a/wifi/TEST_MAPPING b/wifi/TEST_MAPPING index 8c515109a309..7ddc30872cb9 100644 --- a/wifi/TEST_MAPPING +++ b/wifi/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit": [ + "presubmit-large": [ { "name": "CtsWifiTestCases", "options": [ @@ -11,9 +11,6 @@ ], "mainline-presubmit": [ { - "name": "FrameworksWifiApiTests[com.google.android.wifi.apex]" - }, - { "name": "CtsWifiTestCases[com.google.android.wifi.apex]", "options": [ { diff --git a/wifi/api/current.txt b/wifi/api/current.txt index ce2b8ca4f2cd..e11b33efcc33 100644 --- a/wifi/api/current.txt +++ b/wifi/api/current.txt @@ -332,6 +332,7 @@ package android.net.wifi { method public boolean is5GHzBandSupported(); method public boolean is6GHzBandSupported(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isAutoWakeupEnabled(); + method public boolean isBridgedApConcurrencySupported(); method @Deprecated public boolean isDeviceToApRttSupported(); method public boolean isEasyConnectSupported(); method public boolean isEnhancedOpenSupported(); @@ -342,6 +343,7 @@ package android.net.wifi { method @Deprecated public boolean isScanAlwaysAvailable(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isScanThrottleEnabled(); method public boolean isStaApConcurrencySupported(); + method public boolean isStaBridgedApConcurrencySupported(); method public boolean isTdlsSupported(); method public boolean isWapiSupported(); method public boolean isWifiEnabled(); diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt index 48d9fd453b05..eba744330a2e 100644 --- a/wifi/api/system-current.txt +++ b/wifi/api/system-current.txt @@ -483,6 +483,7 @@ package android.net.wifi { method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(@NonNull java.util.List<android.net.wifi.ScanResult>); method public boolean is60GHzBandSupported(); method public boolean isApMacRandomizationSupported(); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean isCarrierNetworkOffloadEnabled(int, boolean); method public boolean isConnectedMacRandomizationSupported(); method @Deprecated public boolean isDeviceToDeviceRttSupported(); method public boolean isPortableHotspotSupported(); @@ -501,6 +502,7 @@ package android.net.wifi { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData(); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void setCarrierNetworkOffloadEnabled(int, boolean, boolean); method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS) public void setCoexUnsafeChannels(@NonNull java.util.Set<android.net.wifi.CoexUnsafeChannel>, int); method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean); diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 6dee751f5894..866e913800fd 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -296,4 +296,8 @@ interface IWifiManager void startTemporarilyDisablingAllNonCarrierMergedWifi(int subId); void stopTemporarilyDisablingAllNonCarrierMergedWifi(); + + void setCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged, boolean enabled); + + boolean isCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged); } diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index fddc8899a0c8..226d1a368223 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -935,6 +935,9 @@ public final class SoftApConfiguration implements Parcelable { * on the requested bands (if possible). * <p> * + * Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine + * whether or not concurrent APs are supported. + * * @param bands Array of the {@link #BandType}. * @return Builder for chaining. * @throws IllegalArgumentException when more than 2 bands are set or an invalid band type @@ -1007,6 +1010,9 @@ public final class SoftApConfiguration implements Parcelable { * The {@link SoftApCapability#getSupportedChannelList(int)} can be used to obtain * valid channels in each band. * + * Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine + * whether or not concurrent APs are supported. + * * <p> * If not set, the default for the channel is the special value 0 which has the framework * auto-select a valid channel from the band configured with {@link #setBands(int[])}. diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 90edc4523b7b..e127ea99c716 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -1335,6 +1335,16 @@ public class WifiEnterpriseConfig implements Parcelable { } /** + * Initialize the value of the app installed device key and cert flag. + * + * @param isAppInstalledDeviceKeyAndCert true or false + * @hide + */ + public void initIsAppInstalledDeviceKeyAndCert(boolean isAppInstalledDeviceKeyAndCert) { + mIsAppInstalledDeviceKeyAndCert = isAppInstalledDeviceKeyAndCert; + } + + /** * Check if CA certificate was installed by an app, or manually (not by an app). If true, * CA certificate will be removed from key storage when this network is removed. If not, * then certificates and keys remain persistent until the user manually removes them. @@ -1348,6 +1358,16 @@ public class WifiEnterpriseConfig implements Parcelable { } /** + * Initialize the value of the app installed root CA cert flag. + * + * @param isAppInstalledCaCert true or false + * @hide + */ + public void initIsAppInstalledCaCert(boolean isAppInstalledCaCert) { + mIsAppInstalledCaCert = isAppInstalledCaCert; + } + + /** * Set the OCSP type. * @param ocsp is one of {@link ##OCSP_NONE}, {@link #OCSP_REQUEST_CERT_STATUS}, * {@link #OCSP_REQUIRE_CERT_STATUS} or diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index b8fa1e18ed28..2b931a380f43 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -53,6 +53,7 @@ import android.os.Looper; import android.os.RemoteException; import android.os.WorkSource; import android.os.connectivity.WifiActivityEnergyInfo; +import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.CloseGuard; @@ -2488,6 +2489,12 @@ public class WifiManager { /** @hide */ public static final long WIFI_FEATURE_SAE_PK = 0x10000000000L; // SAE-PK + /** @hide */ + public static final long WIFI_FEATURE_STA_BRIDGED_AP = 0x20000000000L; // STA + Bridged AP + + /** @hide */ + public static final long WIFI_FEATURE_BRIDGED_AP = 0x40000000000L; // Bridged AP + private long getSupportedFeatures() { try { return mService.getSupportedFeatures(); @@ -2688,6 +2695,40 @@ public class WifiManager { } /** + * Query whether the device supports Station (STA) + Bridged access point (AP) + * concurrency or not. + * + * The bridged AP support means that the device supports AP + AP concurrency with the 2 APs + * bridged together. + * + * See {@link SoftApConfiguration.Builder#setBands(int[])} + * or {@link SoftApConfiguration.Builder#setChannels(SparseIntArray)} to configure bridged AP + * when the bridged AP supported. + * + * @return true if this device supports STA + bridged AP concurrency, false otherwise. + */ + public boolean isStaBridgedApConcurrencySupported() { + return isFeatureSupported(WIFI_FEATURE_STA_BRIDGED_AP); + } + + /** + * Query whether the device supports Bridged Access point (AP) concurrency or not. + * + * The bridged AP support means that the device supports AP + AP concurrency with the 2 APs + * bridged together. + * + * See {@link SoftApConfiguration.Builder#setBands(int[])} + * or {@link SoftApConfiguration.Builder#setChannels(SparseIntArray)} to configure bridged AP + * when the bridged AP supported. + * + * @return true if this device supports bridged AP concurrency, false otherwise. + */ + public boolean isBridgedApConcurrencySupported() { + return isFeatureSupported(WIFI_FEATURE_BRIDGED_AP); + } + + + /** * Interface for Wi-Fi activity energy info listener. Should be implemented by applications and * set when calling {@link WifiManager#getWifiActivityEnergyInfoAsync}. * @@ -6721,4 +6762,63 @@ public class WifiManager { throw e.rethrowFromSystemServer(); } } + + /** + * Sets the state of carrier offload on merged or unmerged networks for specified subscription. + * + * <p> + * When a subscription's carrier network offload is disabled, all network suggestions related to + * this subscription will not be considered for auto join. + * <p> + * If calling app want disable all carrier network offload from a specified subscription, should + * call this API twice to disable both merged and unmerged carrier network suggestions. + * + * @param subscriptionId See {@link SubscriptionInfo#getSubscriptionId()}. + * @param merged True for carrier merged network, false otherwise. + * See {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)} + * @param enabled True for enable carrier network offload, false otherwise. + * @see #isCarrierNetworkOffloadEnabled(int, boolean) + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) + public void setCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged, + boolean enabled) { + if (!SdkLevel.isAtLeastS()) { + throw new UnsupportedOperationException(); + } + try { + mService.setCarrierNetworkOffloadEnabled(subscriptionId, merged, enabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the carrier network offload state for merged or unmerged networks for specified + * subscription. + * @param subscriptionId subscription ID see {@link SubscriptionInfo#getSubscriptionId()} + * @param merged True for carrier merged network, false otherwise. + * See {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)} + * @return True to indicate that carrier network offload is enabled, false otherwise. + * @see #setCarrierNetworkOffloadEnabled(int, boolean, boolean) + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) + public boolean isCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged) { + if (!SdkLevel.isAtLeastS()) { + throw new UnsupportedOperationException(); + } + try { + return mService.isCarrierNetworkOffloadEnabled(subscriptionId, merged); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index e606d533a532..b7450c538ff8 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -917,6 +917,9 @@ public final class WifiNetworkSuggestion implements Parcelable { mPasspointConfiguration.setCarrierId(mCarrierId); mPasspointConfiguration.setSubscriptionId(mSubscriptionId); mPasspointConfiguration.setMeteredOverride(wifiConfiguration.meteredOverride); + mPasspointConfiguration.setOemPrivate(mIsNetworkOemPrivate); + mPasspointConfiguration.setOemPaid(mIsNetworkOemPaid); + mPasspointConfiguration.setCarrierMerged(mIsCarrierMerged); wifiConfiguration.macRandomizationSetting = mIsEnhancedMacRandomizationEnabled ? WifiConfiguration.RANDOMIZATION_ENHANCED : WifiConfiguration.RANDOMIZATION_PERSISTENT; diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index e19b095b27eb..540bf2a72b34 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -774,9 +774,13 @@ public class WifiAwareManager { (byte[]) msg.obj); break; case CALLBACK_MATCH_EXPIRED: + if (!SdkLevel.isAtLeastS()) { + break; + } mOriginalCallback .onServiceLost(new PeerHandle(msg.arg1), WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE); + break; } } }; diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index 357c5bcfa265..006fbaa028bd 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -474,6 +474,27 @@ public final class PasspointConfiguration implements Parcelable { */ private boolean mIsEnhancedMacRandomizationEnabled = false; + + /** + * Indicate whether the network is oem paid or not. Networks are considered oem paid + * if the corresponding connection is only available to system apps. + * @hide + */ + private boolean mIsOemPaid; + + /** + * Indicate whether the network is oem private or not. Networks are considered oem private + * if the corresponding connection is only available to system apps. + * @hide + */ + private boolean mIsOemPrivate; + + /** + * Indicate whether or not the network is a carrier merged network. + * @hide + */ + private boolean mIsCarrierMerged; + /** * Indicates if the end user has expressed an explicit opinion about the * meteredness of this network, such as through the Settings app. @@ -589,6 +610,54 @@ public final class PasspointConfiguration implements Parcelable { } /** + * Set whether the network is oem paid or not. + * @hide + */ + public void setOemPaid(boolean isOemPaid) { + mIsOemPaid = isOemPaid; + } + + /** + * Get whether the network is oem paid or not. + * @hide + */ + public boolean isOemPaid() { + return mIsOemPaid; + } + + /** + * Set whether the network is oem private or not. + * @hide + */ + public void setOemPrivate(boolean isOemPrivate) { + mIsOemPrivate = isOemPrivate; + } + + /** + * Get whether the network is oem private or not. + * @hide + */ + public boolean isOemPrivate() { + return mIsOemPrivate; + } + + /** + * Set whether the network is carrier merged or not. + * @hide + */ + public void setCarrierMerged(boolean isCarrierMerged) { + mIsCarrierMerged = isCarrierMerged; + } + + /** + * Get whether the network is carrier merged or not. + * @hide + */ + public boolean isCarrierMerged() { + return mIsCarrierMerged; + } + + /** * Constructor for creating PasspointConfiguration with default values. */ public PasspointConfiguration() {} @@ -635,6 +704,9 @@ public final class PasspointConfiguration implements Parcelable { mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled; mIsEnhancedMacRandomizationEnabled = source.mIsEnhancedMacRandomizationEnabled; mMeteredOverride = source.mMeteredOverride; + mIsCarrierMerged = source.mIsCarrierMerged; + mIsOemPaid = source.mIsOemPaid; + mIsOemPrivate = source.mIsOemPrivate; } @Override @@ -669,6 +741,9 @@ public final class PasspointConfiguration implements Parcelable { dest.writeBoolean(mIsEnhancedMacRandomizationEnabled); dest.writeInt(mMeteredOverride); dest.writeInt(mSubscriptionId); + dest.writeBoolean(mIsCarrierMerged); + dest.writeBoolean(mIsOemPaid); + dest.writeBoolean(mIsOemPrivate); } @Override @@ -700,6 +775,9 @@ public final class PasspointConfiguration implements Parcelable { && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes && mCarrierId == that.mCarrierId && mSubscriptionId == that.mSubscriptionId + && mIsOemPrivate == that.mIsOemPrivate + && mIsOemPaid == that.mIsOemPaid + && mIsCarrierMerged == that.mIsCarrierMerged && mIsAutojoinEnabled == that.mIsAutojoinEnabled && mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled && mIsEnhancedMacRandomizationEnabled == that.mIsEnhancedMacRandomizationEnabled @@ -715,7 +793,8 @@ public final class PasspointConfiguration implements Parcelable { mSubscriptionExpirationTimeMillis, mUsageLimitUsageTimePeriodInMinutes, mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes, mServiceFriendlyNames, mCarrierId, mIsAutojoinEnabled, mIsMacRandomizationEnabled, - mIsEnhancedMacRandomizationEnabled, mMeteredOverride, mSubscriptionId); + mIsEnhancedMacRandomizationEnabled, mMeteredOverride, mSubscriptionId, + mIsCarrierMerged, mIsOemPaid, mIsOemPrivate); } @Override @@ -774,6 +853,9 @@ public final class PasspointConfiguration implements Parcelable { builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled); builder.append("mIsEnhancedMacRandomizationEnabled:" + mIsEnhancedMacRandomizationEnabled); builder.append("mMeteredOverride:" + mMeteredOverride); + builder.append("mIsCarrierMerged:" + mIsCarrierMerged); + builder.append("mIsOemPaid:" + mIsOemPaid); + builder.append("mIsOemPrivate" + mIsOemPrivate); return builder.toString(); } @@ -884,6 +966,10 @@ public final class PasspointConfiguration implements Parcelable { config.mIsEnhancedMacRandomizationEnabled = in.readBoolean(); config.mMeteredOverride = in.readInt(); config.mSubscriptionId = in.readInt(); + config.mIsCarrierMerged = in.readBoolean(); + config.mIsOemPaid = in.readBoolean(); + config.mIsOemPrivate = in.readBoolean(); + return config; } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index 39f6f57b05b3..52e91394d8bf 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -136,6 +136,7 @@ public class WifiManagerTest { private static final String[] TEST_MAC_ADDRESSES = {"da:a1:19:0:0:0"}; private static final int TEST_AP_FREQUENCY = 2412; private static final int TEST_AP_BANDWIDTH = SoftApInfo.CHANNEL_WIDTH_20MHZ; + private static final int TEST_SUB_ID = 3; @Mock Context mContext; @Mock android.net.wifi.IWifiManager mWifiService; @@ -2583,10 +2584,29 @@ public class WifiManagerTest { @Test public void testGetNetworkSuggestionUserApprovalStatus() throws Exception { + assumeTrue(SdkLevel.isAtLeastS()); + when(mWifiService.getNetworkSuggestionUserApprovalStatus(TEST_PACKAGE_NAME)) .thenReturn(WifiManager.STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER); assertEquals(WifiManager.STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER, mWifiManager.getNetworkSuggestionUserApprovalStatus()); verify(mWifiService).getNetworkSuggestionUserApprovalStatus(TEST_PACKAGE_NAME); } + + @Test + public void testSetCarrierNetworkOffload() throws Exception { + assumeTrue(SdkLevel.isAtLeastS()); + mWifiManager.setCarrierNetworkOffloadEnabled(TEST_SUB_ID, true, false); + verify(mWifiService).setCarrierNetworkOffloadEnabled(TEST_SUB_ID, + true, false); + } + + @Test + public void testGetCarrierNetworkOffload() throws Exception { + assumeTrue(SdkLevel.isAtLeastS()); + when(mWifiService.isCarrierNetworkOffloadEnabled(TEST_SUB_ID, false)).thenReturn(true); + assertTrue(mWifiManager.isCarrierNetworkOffloadEnabled(TEST_SUB_ID, false)); + verify(mWifiService).isCarrierNetworkOffloadEnabled(TEST_SUB_ID, false); + } + } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java index 643a78c38f91..5e829188f93f 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -809,7 +809,7 @@ public class WifiNetworkSuggestionTest { /** * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception * when both {@link WifiNetworkSuggestion.Builder#setWpa3Passphrase(String)} and - * {@link WifiNetworkSuggestion.Builderi + * {@link WifiNetworkSuggestion.Builder * #setWpa3EnterpriseStandardModeConfig(WifiEnterpriseConfig)} * are invoked. */ @@ -1310,6 +1310,7 @@ public class WifiNetworkSuggestionTest { .build(); assertTrue(suggestion.isOemPaid()); assertFalse(suggestion.isUserAllowedToManuallyConnect); + assertTrue(suggestion.getPasspointConfig().isOemPaid()); } /** @@ -1345,6 +1346,7 @@ public class WifiNetworkSuggestionTest { .build(); assertTrue(suggestion.isOemPrivate()); assertFalse(suggestion.isUserAllowedToManuallyConnect); + assertTrue(suggestion.getPasspointConfig().isOemPrivate()); } /** @@ -1439,6 +1441,25 @@ public class WifiNetworkSuggestionTest { } /** + * Validate {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)} (boolean)} set the + * correct value to the passpoint network. + */ + @Test + public void testSetCarrierMergedNetworkOnPasspointNetwork() { + assumeTrue(SdkLevel.isAtLeastS()); + + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration) + .setSubscriptionId(1) + .setCarrierMerged(true) + .setIsMetered(true) + .build(); + assertTrue(suggestion.isCarrierMerged()); + assertTrue(suggestion.getPasspointConfig().isCarrierMerged()); + } + + /** * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception * when set both {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)} (boolean)} * to true on a network is not metered. |