diff options
451 files changed, 10894 insertions, 5608 deletions
diff --git a/Android.bp b/Android.bp index 9690969305bf..ee5e992935ae 100644 --- a/Android.bp +++ b/Android.bp @@ -917,7 +917,6 @@ filegroup { "core/java/com/android/internal/util/RingBufferIndices.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", - "core/java/com/android/internal/util/TrafficStatsConstants.java", "core/java/com/android/internal/util/WakeupMessage.java", "core/java/com/android/internal/util/TokenBucket.java", ], @@ -944,7 +943,6 @@ filegroup { "core/java/com/android/internal/util/MessageUtils.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", - "core/java/com/android/internal/util/TrafficStatsConstants.java", "core/java/com/android/internal/util/WakeupMessage.java", ], } diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index 386c6e23f865..f6bfaa356799 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -116,7 +116,6 @@ package android.app.appsearch { public abstract static class AppSearchSchema.PropertyConfig { method public int getCardinality(); - method public int getDataType(); method @NonNull public String getName(); field public static final int CARDINALITY_OPTIONAL = 2; // 0x2 field public static final int CARDINALITY_REPEATED = 1; // 0x1 @@ -181,14 +180,15 @@ package android.app.appsearch { method public int getScore(); method public long getTtlMillis(); method @NonNull public String getUri(); - field public static final String DEFAULT_NAMESPACE = ""; + field @Deprecated public static final String DEFAULT_NAMESPACE = ""; } public static class GenericDocument.Builder<BuilderType extends android.app.appsearch.GenericDocument.Builder> { - ctor public GenericDocument.Builder(@NonNull String, @NonNull String); + ctor @Deprecated public GenericDocument.Builder(@NonNull String, @NonNull String); + ctor public GenericDocument.Builder(@NonNull String, @NonNull String, @NonNull String); method @NonNull public android.app.appsearch.GenericDocument build(); method @NonNull public BuilderType setCreationTimestampMillis(long); - method @NonNull public BuilderType setNamespace(@NonNull String); + method @Deprecated @NonNull public BuilderType setNamespace(@NonNull String); method @NonNull public BuilderType setPropertyBoolean(@NonNull String, @NonNull boolean...); method @NonNull public BuilderType setPropertyBytes(@NonNull String, @NonNull byte[]...); method @NonNull public BuilderType setPropertyDocument(@NonNull String, @NonNull android.app.appsearch.GenericDocument...); @@ -207,12 +207,13 @@ package android.app.appsearch { } public static final class GetByUriRequest.Builder { - ctor public GetByUriRequest.Builder(); + ctor @Deprecated public GetByUriRequest.Builder(); + ctor public GetByUriRequest.Builder(@NonNull String); method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>); method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...); method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@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); + method @Deprecated @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String); } public class GlobalSearchSession implements java.io.Closeable { @@ -243,11 +244,12 @@ package android.app.appsearch { } public static final class RemoveByUriRequest.Builder { - ctor public RemoveByUriRequest.Builder(); + ctor @Deprecated public RemoveByUriRequest.Builder(); + ctor public RemoveByUriRequest.Builder(@NonNull String); method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.lang.String...); method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@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); + method @Deprecated @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String); } public final class ReportUsageRequest { @@ -257,30 +259,50 @@ package android.app.appsearch { } public static final class ReportUsageRequest.Builder { - ctor public ReportUsageRequest.Builder(); + ctor @Deprecated public ReportUsageRequest.Builder(); + ctor public ReportUsageRequest.Builder(@NonNull String); method @NonNull public android.app.appsearch.ReportUsageRequest build(); - method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String); + method @Deprecated @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String); method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUri(@NonNull String); method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimeMillis(long); } public final class SearchResult { method @NonNull public String getDatabaseName(); - method @NonNull public android.app.appsearch.GenericDocument getDocument(); + method @Deprecated @NonNull public android.app.appsearch.GenericDocument getDocument(); + method @NonNull public android.app.appsearch.GenericDocument getGenericDocument(); method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches(); method @NonNull public String getPackageName(); } + public static final class SearchResult.Builder { + ctor public SearchResult.Builder(@NonNull String, @NonNull String); + method @NonNull public android.app.appsearch.SearchResult.Builder addMatch(@NonNull android.app.appsearch.SearchResult.MatchInfo); + method @NonNull public android.app.appsearch.SearchResult build(); + method @NonNull public android.app.appsearch.SearchResult.Builder setGenericDocument(@NonNull android.app.appsearch.GenericDocument); + } + public static final class SearchResult.MatchInfo { method @NonNull public CharSequence getExactMatch(); - method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition(); + method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition(); + method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchRange(); method @NonNull public String getFullText(); method @NonNull public String getPropertyPath(); method @NonNull public CharSequence getSnippet(); - method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition(); + method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition(); + method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetRange(); + } + + public static final class SearchResult.MatchInfo.Builder { + ctor public SearchResult.MatchInfo.Builder(); + method @NonNull public android.app.appsearch.SearchResult.MatchInfo build(); + method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setExactMatchRange(@NonNull android.app.appsearch.SearchResult.MatchRange); + method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setPropertyPath(@NonNull String); + method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setSnippetRange(@NonNull android.app.appsearch.SearchResult.MatchRange); } public static final class SearchResult.MatchRange { + ctor public SearchResult.MatchRange(int, int); method public int getEnd(); method public int getStart(); } @@ -362,6 +384,19 @@ package android.app.appsearch { method @NonNull public java.util.List<android.app.appsearch.SetSchemaResponse.MigrationFailure> getMigrationFailures(); } + public static final class SetSchemaResponse.Builder { + ctor public SetSchemaResponse.Builder(); + method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedType(@NonNull String); + method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedTypes(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleType(@NonNull String); + method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleTypes(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedType(@NonNull String); + method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedTypes(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailure(@NonNull android.app.appsearch.SetSchemaResponse.MigrationFailure); + method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailures(@NonNull java.util.Collection<android.app.appsearch.SetSchemaResponse.MigrationFailure>); + method @NonNull public android.app.appsearch.SetSchemaResponse build(); + } + public static class SetSchemaResponse.MigrationFailure { method @NonNull public android.app.appsearch.AppSearchResult<java.lang.Void> getAppSearchResult(); method @NonNull public String getNamespace(); @@ -369,11 +404,23 @@ package android.app.appsearch { method @NonNull public String getUri(); } + public static final class SetSchemaResponse.MigrationFailure.Builder { + ctor public SetSchemaResponse.MigrationFailure.Builder(); + method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure build(); + method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setAppSearchResult(@NonNull android.app.appsearch.AppSearchResult<java.lang.Void>); + method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setNamespace(@NonNull String); + method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setSchemaType(@NonNull String); + method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setUri(@NonNull String); + } + } package android.app.appsearch.exceptions { public class AppSearchException extends java.lang.Exception { + ctor public AppSearchException(int); + ctor public AppSearchException(int, @Nullable String); + ctor public AppSearchException(int, @Nullable String, @Nullable Throwable); method public int getResultCode(); method @NonNull public <T> android.app.appsearch.AppSearchResult<T> toAppSearchResult(); } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java index d3949047ec5d..77740f88de7a 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java @@ -152,14 +152,14 @@ public class AppSearchEmail extends GenericDocument { /** The builder class for {@link AppSearchEmail}. */ public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> { - /** * Creates a new {@link AppSearchEmail.Builder} * + * @param namespace The namespace of the Email. * @param uri The Uri of the Email. */ - public Builder(@NonNull String uri) { - super(uri, SCHEMA_TYPE); + public Builder(@NonNull String namespace, @NonNull String uri) { + super(namespace, uri, SCHEMA_TYPE); } /** Sets the from address of {@link AppSearchEmail} */ diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java index 8bf438d43cd1..2cf52716cffc 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java @@ -309,7 +309,11 @@ public final class AppSearchSchema { return mBundle.getString(NAME_FIELD, ""); } - /** Returns the type of data the property contains (e.g. string, int, bytes, etc). */ + /** + * Returns the type of data the property contains (e.g. string, int, bytes, etc). + * + * @hide + */ public @DataType int getDataType() { return mBundle.getInt(DATA_TYPE_FIELD, -1); } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java index 72bb9f3d07c8..4ce95ea358f4 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java @@ -46,8 +46,14 @@ import java.util.Set; public class GenericDocument { private static final String TAG = "AppSearchGenericDocumen"; - /** The default empty namespace. */ - public static final String DEFAULT_NAMESPACE = ""; + /** + * The default empty namespace. + * + * <p>TODO(b/181887768): This exists only for dogfooder transition and must be removed. + * + * @deprecated This exists only for dogfooder transition and must be removed. + */ + @Deprecated public static final String DEFAULT_NAMESPACE = ""; /** The maximum number of elements in a repeatable field. */ private static final int MAX_REPEATED_PROPERTY_LENGTH = 100; @@ -141,7 +147,7 @@ public class GenericDocument { /** Returns the namespace of the {@link GenericDocument}. */ @NonNull public String getNamespace() { - return mBundle.getString(NAMESPACE_FIELD, DEFAULT_NAMESPACE); + return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ ""); } /** Returns the {@link AppSearchSchema} type of the {@link GenericDocument}. */ @@ -579,6 +585,9 @@ public class GenericDocument { * * <p>Once {@link #build} is called, the instance can no longer be used. * + * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be + * removed. + * * @param uri the URI to set for the {@link GenericDocument}. * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The * provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema} @@ -586,7 +595,10 @@ public class GenericDocument { * using {@link AppSearchSession#put}. Otherwise, the document will be rejected by * {@link AppSearchSession#put} with result code {@link * AppSearchResult#RESULT_NOT_FOUND}. + * @deprecated Please supply the namespace in {@link #Builder(String, String, String)} + * instead. This method exists only for dogfooder transition and must be removed. */ + @Deprecated @SuppressWarnings("unchecked") public Builder(@NonNull String uri, @NonNull String schemaType) { Preconditions.checkNotNull(uri); @@ -604,6 +616,41 @@ public class GenericDocument { } /** + * Creates a new {@link GenericDocument.Builder}. + * + * <p>Once {@link #build} is called, the instance can no longer be used. + * + * <p>URIs are unique within a namespace. + * + * <p>The number of namespaces per app should be kept small for efficiency reasons. + * + * @param namespace the namespace to set for the {@link GenericDocument}. + * @param uri the URI to set for the {@link GenericDocument}. + * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The + * provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema} + * prior to inserting a document of this {@code schemaType} into the AppSearch index + * using {@link AppSearchSession#put}. Otherwise, the document will be rejected by + * {@link AppSearchSession#put} with result code {@link + * AppSearchResult#RESULT_NOT_FOUND}. + */ + @SuppressWarnings("unchecked") + public Builder(@NonNull String namespace, @NonNull String uri, @NonNull String schemaType) { + Preconditions.checkNotNull(namespace); + Preconditions.checkNotNull(uri); + Preconditions.checkNotNull(schemaType); + mBuilderTypeInstance = (BuilderType) this; + mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); + mBundle.putString(GenericDocument.URI_FIELD, uri); + mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType); + // Set current timestamp for creation timestamp by default. + mBundle.putLong( + GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis()); + mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS); + mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE); + mBundle.putBundle(PROPERTIES_FIELD, mProperties); + } + + /** * Sets the app-defined namespace this document resides in. No special values are reserved * or understood by the infrastructure. * @@ -611,8 +658,14 @@ public class GenericDocument { * * <p>The number of namespaces per app should be kept small for efficiency reasons. * + * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be + * removed. + * * @throws IllegalStateException if the builder has already been used. + * @deprecated Please supply the namespace in {@link #Builder(String, String, String)} + * instead. This method exists only for dogfooder transition and must be removed. */ + @Deprecated @NonNull public BuilderType setNamespace(@NonNull String namespace) { Preconditions.checkState(!mBuilt, "Builder has already been used"); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java index 17266f82b603..6881a27d6846 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java @@ -107,19 +107,40 @@ public final class GetByUriRequest { * <p>Once {@link #build} is called, the instance can no longer be used. */ public static final class Builder { - private String mNamespace = GenericDocument.DEFAULT_NAMESPACE; + private String mNamespace; private final Set<String> mUris = new ArraySet<>(); private final Map<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>(); private boolean mBuilt = false; /** + * TODO(b/181887768): This method exists only for dogfooder transition and must be removed. + * + * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method + * exists only for dogfooder transition and must be removed. + */ + @Deprecated + public Builder() { + mNamespace = GenericDocument.DEFAULT_NAMESPACE; + } + + /** Creates a {@link GetByUriRequest.Builder} instance. */ + public Builder(@NonNull String namespace) { + mNamespace = Preconditions.checkNotNull(namespace); + } + + /** * Sets the namespace to retrieve documents for. * - * <p>If this is not called, the namespace defaults to {@link - * GenericDocument#DEFAULT_NAMESPACE}. + * <p>If this is not called, the namespace defaults to an empty string. + * + * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be + * removed. * * @throws IllegalStateException if the builder has already been used. + * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method + * exists only for dogfooder transition and must */ + @Deprecated @NonNull public Builder setNamespace(@NonNull String namespace) { Preconditions.checkState(!mBuilt, "Builder has already been used"); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java index 39b53b604abb..455cf3a26b50 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java @@ -59,17 +59,39 @@ public final class RemoveByUriRequest { * <p>Once {@link #build} is called, the instance can no longer be used. */ public static final class Builder { - private String mNamespace = GenericDocument.DEFAULT_NAMESPACE; + private String mNamespace; private final Set<String> mUris = new ArraySet<>(); private boolean mBuilt = false; /** + * TODO(b/181887768): This method exists only for dogfooder transition and must be removed. + * + * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method + * exists only for dogfooder transition and must be removed. + */ + @Deprecated + public Builder() { + mNamespace = GenericDocument.DEFAULT_NAMESPACE; + } + + /** Creates a {@link RemoveByUriRequest.Builder} instance. */ + public Builder(@NonNull String namespace) { + mNamespace = Preconditions.checkNotNull(namespace); + } + + /** * Sets the namespace to remove documents for. * - * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}. + * <p>If this is not set, it defaults to an empty string. + * + * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be + * removed. * * @throws IllegalStateException if the builder has already been used. + * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method + * exists only for dogfooder transition and must */ + @Deprecated @NonNull public Builder setNamespace(@NonNull String namespace) { Preconditions.checkState(!mBuilt, "Builder has already been used"); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java index 2bfcf2855430..2cd08c631006 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java @@ -62,18 +62,40 @@ public final class ReportUsageRequest { /** Builder for {@link ReportUsageRequest} objects. */ public static final class Builder { - private String mNamespace = GenericDocument.DEFAULT_NAMESPACE; + private String mNamespace; private String mUri; private Long mUsageTimeMillis; private boolean mBuilt = false; /** + * TODO(b/181887768): This method exists only for dogfooder transition and must be removed. + * + * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method + * exists only for dogfooder transition and must be removed. + */ + @Deprecated + public Builder() { + mNamespace = GenericDocument.DEFAULT_NAMESPACE; + } + + /** Creates a {@link ReportUsageRequest.Builder} instance. */ + public Builder(@NonNull String namespace) { + mNamespace = Preconditions.checkNotNull(namespace); + } + + /** * Sets which namespace the document being used belongs to. * - * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}. + * <p>If this is not set, it defaults to an empty string. + * + * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be + * removed. * * @throws IllegalStateException if the builder has already been used + * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method + * exists only for dogfooder transition and must */ + @Deprecated @NonNull public ReportUsageRequest.Builder setNamespace(@NonNull String namespace) { Preconditions.checkState(!mBuilt, "Builder has already been used"); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java index f34034b1c5c0..cb20849dd36f 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java @@ -32,7 +32,7 @@ import java.util.Objects; * <p>This allows clients to obtain: * * <ul> - * <li>The document which matched, using {@link #getDocument} + * <li>The document which matched, using {@link #getGenericDocument} * <li>Information about which properties in the document matched, and "snippet" information * containing textual summaries of the document's matches, using {@link #getMatches} * </ul> @@ -43,17 +43,10 @@ import java.util.Objects; * @see SearchResults */ public final class SearchResult { - /** @hide */ - public static final String DOCUMENT_FIELD = "document"; - - /** @hide */ - public static final String MATCHES_FIELD = "matches"; - - /** @hide */ - public static final String PACKAGE_NAME_FIELD = "packageName"; - - /** @hide */ - public static final String DATABASE_NAME_FIELD = "databaseName"; + static final String DOCUMENT_FIELD = "document"; + static final String MATCHES_FIELD = "matches"; + static final String PACKAGE_NAME_FIELD = "packageName"; + static final String DATABASE_NAME_FIELD = "databaseName"; @NonNull private final Bundle mBundle; @@ -74,13 +67,20 @@ public final class SearchResult { return mBundle; } + /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */ + @NonNull + @Deprecated + public GenericDocument getDocument() { + return getGenericDocument(); + } + /** * Contains the matching {@link GenericDocument}. * * @return Document object which matched the query. */ @NonNull - public GenericDocument getDocument() { + public GenericDocument getGenericDocument() { if (mDocument == null) { mDocument = new GenericDocument( @@ -104,7 +104,7 @@ public final class SearchResult { Preconditions.checkNotNull(mBundle.getParcelableArrayList(MATCHES_FIELD)); mMatches = new ArrayList<>(matchBundles.size()); for (int i = 0; i < matchBundles.size(); i++) { - MatchInfo matchInfo = new MatchInfo(getDocument(), matchBundles.get(i)); + MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument()); mMatches.add(matchInfo); } } @@ -124,13 +124,69 @@ public final class SearchResult { /** * Contains the database name that stored the {@link GenericDocument}. * - * @return Database name that stored the document + * @return Name of the database within which the document is stored */ @NonNull public String getDatabaseName() { return Preconditions.checkNotNull(mBundle.getString(DATABASE_NAME_FIELD)); } + /** Builder for {@link SearchResult} objects. */ + public static final class Builder { + private final Bundle mBundle = new Bundle(); + private final ArrayList<Bundle> mMatchInfos = new ArrayList<>(); + + private boolean mBuilt; + + /** + * Constructs a new builder for {@link SearchResult} objects. + * + * @param packageName the package name the matched document belongs to + * @param databaseName the database name the matched document belongs to. + */ + public Builder(@NonNull String packageName, @NonNull String databaseName) { + mBundle.putString(PACKAGE_NAME_FIELD, Preconditions.checkNotNull(packageName)); + mBundle.putString(DATABASE_NAME_FIELD, Preconditions.checkNotNull(databaseName)); + } + + /** + * Sets the document which matched. + * + * @throws IllegalStateException if the builder has already been used + */ + @NonNull + public Builder setGenericDocument(@NonNull GenericDocument document) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBundle.putBundle(DOCUMENT_FIELD, document.getBundle()); + return this; + } + + /** Adds another match to this SearchResult. */ + @NonNull + public Builder addMatch(@NonNull MatchInfo matchInfo) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkState( + matchInfo.mDocument == null, + "This MatchInfo is already associated with a SearchResult and can't be " + + "reassigned"); + mMatchInfos.add(matchInfo.mBundle); + return this; + } + + /** + * Constructs a new {@link SearchResult}. + * + * @throws IllegalStateException if the builder has already been used + */ + @NonNull + public SearchResult build() { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBundle.putParcelableArrayList(MATCHES_FIELD, mMatchInfos); + mBuilt = true; + return new SearchResult(mBundle); + } + } + /** * This class represents a match objects for any Snippets that might be present in {@link * SearchResults} from query. Using this class user can get the full text, exact matches and @@ -147,11 +203,11 @@ public final class SearchResult { * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another * nonsense word that’s used a lot is bar." * - * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32] + * <p>{@link MatchInfo#getExactMatchRange()} returns [29, 32] * * <p>{@link MatchInfo#getExactMatch()} returns "foo" * - * <p>{@link MatchInfo#getSnippetPosition()} returns [26, 33] + * <p>{@link MatchInfo#getSnippetRange()} returns [26, 33] * * <p>{@link MatchInfo#getSnippet()} returns "is foo." * @@ -172,11 +228,11 @@ public final class SearchResult { * * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr." * - * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4] + * <p>{@link MatchInfo#getExactMatchRange()} returns [0, 4] * * <p>{@link MatchInfo#getExactMatch()} returns "Test" * - * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9] + * <p>{@link MatchInfo#getSnippetRange()} returns [0, 9] * * <p>{@link MatchInfo#getSnippet()} returns "Test Name" * @@ -186,52 +242,54 @@ public final class SearchResult { * * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com" * - * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20] + * <p>{@link MatchInfo#getExactMatchRange()} returns [0, 20] * * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com" * - * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20] + * <p>{@link MatchInfo#getSnippetRange()} returns [0, 20] * * <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com" */ public static final class MatchInfo { + /** The path of the matching snippet property. */ + private static final String PROPERTY_PATH_FIELD = "propertyPath"; + + private static final String EXACT_MATCH_RANGE_LOWER_FIELD = "exactMatchRangeLower"; + private static final String EXACT_MATCH_RANGE_UPPER_FIELD = "exactMatchRangeUpper"; + private static final String SNIPPET_RANGE_LOWER_FIELD = "snippetRangeLower"; + private static final String SNIPPET_RANGE_UPPER_FIELD = "snippetRangeUpper"; + + private final String mPropertyPath; + final Bundle mBundle; + /** - * The path of the matching snippet property. + * Document which the match comes from. * - * @hide + * <p>If this is {@code null}, methods which require access to the document, like {@link + * #getExactMatch}, will throw {@link NullPointerException}. */ - public static final String PROPERTY_PATH_FIELD = "propertyPath"; + @Nullable final GenericDocument mDocument; - /** @hide */ - public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower"; + /** Full text of the matched property. Populated on first use. */ + @Nullable private String mFullText; - /** @hide */ - public static final String EXACT_MATCH_POSITION_UPPER_FIELD = "exactMatchPositionUpper"; + /** Range of property that exactly matched the query. Populated on first use. */ + @Nullable private MatchRange mExactMatchRange; - /** @hide */ - public static final String WINDOW_POSITION_LOWER_FIELD = "windowPositionLower"; + /** Range of some reasonable amount of context around the query. Populated on first use. */ + @Nullable private MatchRange mWindowRange; - /** @hide */ - public static final String WINDOW_POSITION_UPPER_FIELD = "windowPositionUpper"; - - private final String mFullText; - private final String mPropertyPath; - private final Bundle mBundle; - private MatchRange mExactMatchRange; - private MatchRange mWindowRange; - - MatchInfo(@NonNull GenericDocument document, @NonNull Bundle bundle) { + MatchInfo(@NonNull Bundle bundle, @Nullable GenericDocument document) { mBundle = Preconditions.checkNotNull(bundle); - Preconditions.checkNotNull(document); + mDocument = document; mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD)); - mFullText = getPropertyValues(document, mPropertyPath); } /** * Gets the property path corresponding to the given entry. * - * <p>Property Path: '.' - delimited sequence of property names indicating which property in - * the Document these snippets correspond to. + * <p>A property path is a '.' - delimited sequence of property names indicating which + * property in the document these snippets correspond to. * * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class * example 1 this returns "subject" @@ -249,21 +307,34 @@ public final class SearchResult { */ @NonNull public String getFullText() { + if (mFullText == null) { + Preconditions.checkState( + mDocument != null, + "Document has not been populated; this MatchInfo cannot be used yet"); + mFullText = getPropertyValues(mDocument, mPropertyPath); + } return mFullText; } + /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */ + @NonNull + @Deprecated + public MatchRange getExactMatchPosition() { + return getExactMatchRange(); + } + /** * Gets the exact {@link MatchRange} corresponding to the given entry. * * <p>For class example 1 this returns [29, 32] */ @NonNull - public MatchRange getExactMatchPosition() { + public MatchRange getExactMatchRange() { if (mExactMatchRange == null) { mExactMatchRange = new MatchRange( - mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD), - mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD)); + mBundle.getInt(EXACT_MATCH_RANGE_LOWER_FIELD), + mBundle.getInt(EXACT_MATCH_RANGE_UPPER_FIELD)); } return mExactMatchRange; } @@ -275,7 +346,14 @@ public final class SearchResult { */ @NonNull public CharSequence getExactMatch() { - return getSubstring(getExactMatchPosition()); + return getSubstring(getExactMatchRange()); + } + + /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */ + @NonNull + @Deprecated + public MatchRange getSnippetPosition() { + return getSnippetRange(); } /** @@ -287,12 +365,12 @@ public final class SearchResult { * <p>For class example 1 this returns [29, 41]. */ @NonNull - public MatchRange getSnippetPosition() { + public MatchRange getSnippetRange() { if (mWindowRange == null) { mWindowRange = new MatchRange( - mBundle.getInt(WINDOW_POSITION_LOWER_FIELD), - mBundle.getInt(WINDOW_POSITION_UPPER_FIELD)); + mBundle.getInt(SNIPPET_RANGE_LOWER_FIELD), + mBundle.getInt(SNIPPET_RANGE_UPPER_FIELD)); } return mWindowRange; } @@ -309,7 +387,7 @@ public final class SearchResult { */ @NonNull public CharSequence getSnippet() { - return getSubstring(getSnippetPosition()); + return getSubstring(getSnippetRange()); } private CharSequence getSubstring(MatchRange range) { @@ -331,6 +409,72 @@ public final class SearchResult { // TODO(b/175146044): Return the proper match based on the index in the propertyName. return values[0]; } + + /** Builder for {@link MatchInfo} objects. */ + public static final class Builder { + private final Bundle mBundle = new Bundle(); + private boolean mBuilt = false; + + /** + * Sets the property path corresponding to the given entry. + * + * <p>A property path is a '.' - delimited sequence of property names indicating which + * property in the document these snippets correspond to. + * + * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class + * example 1 this returns "subject" + * + * @throws IllegalStateException if the builder has already been used + */ + @NonNull + public Builder setPropertyPath(@NonNull String propertyPath) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBundle.putString( + SearchResult.MatchInfo.PROPERTY_PATH_FIELD, + Preconditions.checkNotNull(propertyPath)); + return this; + } + + /** + * Sets the exact {@link MatchRange} corresponding to the given entry. + * + * @throws IllegalStateException if the builder has already been used + */ + @NonNull + public Builder setExactMatchRange(@NonNull MatchRange matchRange) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(matchRange); + mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_LOWER_FIELD, matchRange.getStart()); + mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_UPPER_FIELD, matchRange.getEnd()); + return this; + } + + /** + * Sets the snippet {@link MatchRange} corresponding to the given entry. + * + * @throws IllegalStateException if the builder has already been used + */ + @NonNull + public Builder setSnippetRange(@NonNull MatchRange matchRange) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(matchRange); + mBundle.putInt(MatchInfo.SNIPPET_RANGE_LOWER_FIELD, matchRange.getStart()); + mBundle.putInt(MatchInfo.SNIPPET_RANGE_UPPER_FIELD, matchRange.getEnd()); + return this; + } + + /** + * Constructs a new {@link MatchInfo}. + * + * @throws IllegalStateException if the builder has already been used + */ + @NonNull + public MatchInfo build() { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBuilt = true; + return new MatchInfo(mBundle, /*document=*/ null); + } + } } /** @@ -353,7 +497,6 @@ public final class SearchResult { * * @param start The start point (inclusive) * @param end The end point (exclusive) - * @hide */ public MatchRange(int start, int end) { if (start > end) { diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java index a146006f355c..98cd49b79737 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java @@ -160,12 +160,8 @@ public class SetSchemaResponse { .addMigrationFailures(mMigrationFailures); } - /** - * Builder for {@link SetSchemaResponse} objects. - * - * @hide - */ - public static class Builder { + /** Builder for {@link SetSchemaResponse} objects. */ + public static final class Builder { private final ArrayList<MigrationFailure> mMigrationFailures = new ArrayList<>(); private final ArrayList<String> mDeletedTypes = new ArrayList<>(); private final ArrayList<String> mMigratedTypes = new ArrayList<>(); @@ -309,12 +305,8 @@ public class SetSchemaResponse { mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ "")); } - /** - * Builder for {@link MigrationFailure} objects. - * - * @hide - */ - public static class Builder { + /** Builder for {@link MigrationFailure} objects. */ + public static final class Builder { private String mSchemaType; private String mNamespace; private String mUri; diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java index b1a33a478a47..ca4ea2bfd3bb 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java @@ -32,19 +32,27 @@ public class AppSearchException extends Exception { /** * Initializes an {@link AppSearchException} with no message. * - * @hide + * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}. */ public AppSearchException(@AppSearchResult.ResultCode int resultCode) { this(resultCode, /*message=*/ null); } - /** @hide */ + /** + * Initializes an {@link AppSearchException} with a result code and message. + * + * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}. + */ public AppSearchException( @AppSearchResult.ResultCode int resultCode, @Nullable String message) { this(resultCode, message, /*cause=*/ null); } - /** @hide */ + /** + * Initializes an {@link AppSearchException} with a result code, message and cause. + * + * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}. + */ public AppSearchException( @AppSearchResult.ResultCode int resultCode, @Nullable String message, @@ -53,12 +61,16 @@ public class AppSearchException extends Exception { mResultCode = resultCode; } - /** Returns the result code this exception was constructed with. */ + /** + * Returns the result code this exception was constructed with. + * + * @return One of the constants documented in {@link AppSearchResult#getResultCode}. + */ public @AppSearchResult.ResultCode int getResultCode() { return mResultCode; } - /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} */ + /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}. */ @NonNull public <T> AppSearchResult<T> toAppSearchResult() { return AppSearchResult.newFailedResult(mResultCode, getMessage()); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java index a7f1cc4c793f..4b8ce6d2c1d1 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java @@ -93,7 +93,7 @@ class AppSearchMigrationHelperImpl implements AppSearchMigrationHelper { migrator.transform( currentVersion, finalVersion, - searchResultPage.getResults().get(i).getDocument()); + searchResultPage.getResults().get(i).getGenericDocument()); Bundle bundle = newDocument.getBundle(); Parcel parcel = Parcel.obtain(); parcel.writeBundle(bundle); 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 a2386eccc256..d6b9da827515 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 @@ -102,8 +102,8 @@ public final class GenericDocumentToProtoConverter { public static GenericDocument toGenericDocument(@NonNull DocumentProto proto) { Preconditions.checkNotNull(proto); GenericDocument.Builder<?> documentBuilder = - new GenericDocument.Builder<>(proto.getUri(), proto.getSchema()) - .setNamespace(proto.getNamespace()) + new GenericDocument.Builder<>( + proto.getNamespace(), proto.getUri(), proto.getSchema()) .setScore(proto.getScore()) .setTtlMillis(proto.getTtlMs()) .setCreationTimestampMillis(proto.getCreationTimestampMs()); 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 e9852aa1cd41..1d8db7233a7a 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 @@ -58,14 +58,14 @@ public class SearchResultToProtoConverter { @NonNull List<String> databaseNames) { Preconditions.checkArgument( proto.getResultsCount() == packageNames.size(), - "Size of " + "results does not match the number of package names."); + "Size of results does not match the number of package names."); 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( - toSearchResultBundle( - proto.getResults(i), packageNames.get(i), databaseNames.get(i))); + SearchResult result = + toSearchResult(proto.getResults(i), packageNames.get(i), databaseNames.get(i)); + resultBundles.add(result.getBundle()); } bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); return new SearchResultPage(bundle); @@ -80,50 +80,41 @@ public class SearchResultToProtoConverter { * @return A {@link SearchResult} bundle. */ @NonNull - private static Bundle toSearchResultBundle( + private static SearchResult toSearchResult( @NonNull SearchResultProto.ResultProtoOrBuilder proto, @NonNull String packageName, @NonNull String databaseName) { - Bundle bundle = new Bundle(); GenericDocument document = GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument()); - bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle()); - bundle.putString(SearchResult.PACKAGE_NAME_FIELD, packageName); - bundle.putString(SearchResult.DATABASE_NAME_FIELD, databaseName); - - ArrayList<Bundle> matchList = new ArrayList<>(); + SearchResult.Builder builder = + new SearchResult.Builder(packageName, databaseName).setGenericDocument(document); if (proto.hasSnippet()) { for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) { SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i); for (int j = 0; j < entry.getSnippetMatchesCount(); j++) { - Bundle matchInfoBundle = - convertToMatchInfoBundle( - entry.getSnippetMatches(j), entry.getPropertyName()); - matchList.add(matchInfoBundle); + SearchResult.MatchInfo matchInfo = + toMatchInfo(entry.getSnippetMatches(j), entry.getPropertyName()); + builder.addMatch(matchInfo); } } } - bundle.putParcelableArrayList(SearchResult.MATCHES_FIELD, matchList); - - return bundle; + return builder.build(); } - private static Bundle convertToMatchInfoBundle( - SnippetMatchProto snippetMatchProto, String propertyPath) { - Bundle bundle = new Bundle(); - bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, propertyPath); - bundle.putInt( - SearchResult.MatchInfo.EXACT_MATCH_POSITION_LOWER_FIELD, - snippetMatchProto.getExactMatchPosition()); - bundle.putInt( - SearchResult.MatchInfo.EXACT_MATCH_POSITION_UPPER_FIELD, - snippetMatchProto.getExactMatchPosition() + snippetMatchProto.getExactMatchBytes()); - bundle.putInt( - SearchResult.MatchInfo.WINDOW_POSITION_LOWER_FIELD, - snippetMatchProto.getWindowPosition()); - bundle.putInt( - SearchResult.MatchInfo.WINDOW_POSITION_UPPER_FIELD, - snippetMatchProto.getWindowPosition() + snippetMatchProto.getWindowBytes()); - return bundle; + private static SearchResult.MatchInfo toMatchInfo( + @NonNull SnippetMatchProto snippetMatchProto, @NonNull String propertyPath) { + return new SearchResult.MatchInfo.Builder() + .setPropertyPath(propertyPath) + .setExactMatchRange( + new SearchResult.MatchRange( + snippetMatchProto.getExactMatchPosition(), + snippetMatchProto.getExactMatchPosition() + + snippetMatchProto.getExactMatchBytes())) + .setSnippetRange( + new SearchResult.MatchRange( + snippetMatchProto.getWindowPosition(), + snippetMatchProto.getWindowPosition() + + snippetMatchProto.getWindowBytes())) + .build(); } } diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index 41c70f072262..68531b6ee1e5 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I42b89416968565ceb6483b400894f5b49524208c +I1926fb1d13628607f7a513c8149b65dd86c98dd6 diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java index 37717d6837b9..4a3c7a53d43e 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java @@ -47,10 +47,7 @@ public class AppSearchTestUtils { AppSearchBatchResult<String, GenericDocument> result = checkIsBatchResultSuccess( session.getByUri( - new GetByUriRequest.Builder() - .setNamespace(namespace) - .addUris(uris) - .build())); + new GetByUriRequest.Builder(namespace).addUris(uris).build())); assertThat(result.getSuccesses()).hasSize(uris.length); assertThat(result.getFailures()).isEmpty(); List<GenericDocument> list = new ArrayList<>(uris.length); @@ -80,7 +77,7 @@ public class AppSearchTestUtils { List<GenericDocument> documents = new ArrayList<>(); while (results.size() > 0) { for (SearchResult result : results) { - documents.add(result.getDocument()); + documents.add(result.getGenericDocument()); } results = searchResults.getNextPage().get(); } diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 77146e0d1282..78c5b156bc45 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -539,6 +539,11 @@ public class AlarmManager { * scheduled as exact. Applications are strongly discouraged from using exact * alarms unnecessarily as they reduce the OS's ability to minimize battery use. * + * <p> + * Starting with {@link Build.VERSION_CODES#S}, apps require the + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use this + * API. + * * @param type type of alarm. * @param triggerAtMillis time in milliseconds that the alarm should go * off, using the appropriate clock (depending on the alarm type). @@ -558,6 +563,7 @@ public class AlarmManager { * @see #RTC * @see #RTC_WAKEUP */ + @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true) public void setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, null, null, null); @@ -571,7 +577,13 @@ public class AlarmManager { * The OnAlarmListener's {@link OnAlarmListener#onAlarm() onAlarm()} method will be * invoked via the specified target Handler, or on the application's main looper * if {@code null} is passed as the {@code targetHandler} parameter. + * + * <p> + * Starting with {@link Build.VERSION_CODES#S}, apps require the + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use this + * API. */ + @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true) public void setExact(@AlarmType int type, long triggerAtMillis, String tag, OnAlarmListener listener, Handler targetHandler) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, null, listener, tag, diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 6967d819a448..4c8ab9385903 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -59,15 +59,6 @@ import java.util.Objects; * constraint on the JobInfo object that you are creating. Otherwise, the builder would throw an * exception when building. From Android version {@link Build.VERSION_CODES#Q} and onwards, it is * valid to schedule jobs with no constraints. - * <p> Prior to Android version {@link Build.VERSION_CODES#S}, jobs could only have a maximum of 100 - * jobs scheduled at a time. Starting with Android version {@link Build.VERSION_CODES#S}, that limit - * has been increased to 150. Expedited jobs also count towards the limit. - * <p> In Android version {@link Build.VERSION_CODES#LOLLIPOP}, jobs had a maximum execution time - * of one minute. Starting with Android version {@link Build.VERSION_CODES#M} and ending with - * Android version {@link Build.VERSION_CODES#R}, jobs had a maximum execution time of 10 minutes. - * Starting from Android version {@link Build.VERSION_CODES#S}, jobs will still be stopped after - * 10 minutes if the system is busy or needs the resources, but if not, jobs may continue running - * longer than 10 minutes. */ public class JobInfo implements Parcelable { private static String TAG = "JobInfo"; @@ -1471,7 +1462,7 @@ public class JobInfo implements Parcelable { * <ol> * <li>Run as soon as possible</li> * <li>Be less restricted during Doze and battery saver</li> - * <li>Have network access</li> + * <li>Have the same network access as foreground services</li> * <li>Be less likely to be killed than regular jobs</li> * <li>Be subject to background location throttling</li> * </ol> diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java index 361325dae7fd..1f4ef0470ebd 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java @@ -57,6 +57,19 @@ import java.util.List; * {@link android.content.Context#getSystemService * Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}. * + * <p> Prior to Android version {@link android.os.Build.VERSION_CODES#S}, jobs could only have + * a maximum of 100 jobs scheduled at a time. Starting with Android version + * {@link android.os.Build.VERSION_CODES#S}, that limit has been increased to 150. + * Expedited jobs also count towards the limit. + * + * <p> In Android version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, jobs had a maximum + * execution time of one minute. Starting with Android version + * {@link android.os.Build.VERSION_CODES#M} and ending with Android version + * {@link android.os.Build.VERSION_CODES#R}, jobs had a maximum execution time of 10 minutes. + * Starting from Android version {@link android.os.Build.VERSION_CODES#S}, jobs will still be + * stopped after 10 minutes if the system is busy or needs the resources, but if not, jobs + * may continue running longer than 10 minutes. + * * <p class="caution"><strong>Note:</strong> Beginning with API 30 * ({@link android.os.Build.VERSION_CODES#R}), JobScheduler will throttle runaway applications. * Calling {@link #schedule(JobInfo)} and other such methods with very high frequency can have a diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java index 61afadab9b0c..0f3d299291c5 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobService.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java @@ -74,6 +74,7 @@ public abstract class JobService extends Service { /** * Call this to inform the JobScheduler that the job has finished its work. When the * system receives this message, it releases the wakelock being held for the job. + * This does not need to be called if {@link #onStopJob(JobParameters)} has been called. * <p> * You can request that the job be scheduled again by passing {@code true} as * the <code>wantsReschedule</code> parameter. This will apply back-off policy @@ -135,6 +136,8 @@ public abstract class JobService extends Service { /** * This method is called if the system has determined that you must stop execution of your job * even before you've had a chance to call {@link #jobFinished(JobParameters, boolean)}. + * Once this method is called, you no longer need to call + * {@link #jobFinished(JobParameters, boolean)}. * * <p>This will happen if the requirements specified at schedule time are no longer met. For * example you may have requested WiFi with @@ -144,8 +147,8 @@ public abstract class JobService extends Service { * idle maintenance window. You are solely responsible for the behavior of your application * upon receipt of this message; your app will likely start to misbehave if you ignore it. * <p> - * Once this method returns, the system releases the wakelock that it is holding on - * behalf of the job.</p> + * Once this method returns (or times out), the system releases the wakelock that it is holding + * on behalf of the job.</p> * * @param params The parameters identifying this job, as supplied to * the job in the {@link #onStartJob(JobParameters)} callback. diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java index d9a49aa52365..88f21a53f930 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -29,6 +29,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.UserHandleAware; import android.content.Context; import java.lang.annotation.Retention; @@ -173,6 +174,8 @@ public class PowerExemptionManager { public static final int REASON_ALLOWLISTED_PACKAGE = 65; /** @hide */ public static final int REASON_APPOP = 66; + /** @hide */ + public static final int REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD = 67; /* BG-FGS-launch is allowed by temp-allow-list or system-allow-list. Reason code for temp and system allow list starts here. @@ -295,6 +298,11 @@ public class PowerExemptionManager { * @hide */ public static final int REASON_SHELL = 316; + /** + * Media session callbacks. + * @hide + */ + public static final int REASON_MEDIA_SESSION_CALLBACK = 317; /** * The list of BG-FGS-Launch and temp-allow-list reason code. @@ -328,6 +336,7 @@ public class PowerExemptionManager { REASON_EXEMPTED_PACKAGE, REASON_ALLOWLISTED_PACKAGE, REASON_APPOP, + REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD, // temp and system allow list reasons. REASON_GEOFENCING, REASON_PUSH_MESSAGING, @@ -354,6 +363,7 @@ public class PowerExemptionManager { REASON_EVENT_SMS, REASON_EVENT_MMS, REASON_SHELL, + REASON_MEDIA_SESSION_CALLBACK, }) @Retention(RetentionPolicy.SOURCE) public @interface ReasonCode {} @@ -451,6 +461,7 @@ public class PowerExemptionManager { * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure. * @param reason a optional human readable reason string, could be null or empty string. */ + @UserHandleAware @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void addToTemporaryAllowList(@NonNull String packageName, long durationMs, @ReasonCode int reasonCode, @Nullable String reason) { @@ -474,6 +485,7 @@ public class PowerExemptionManager { * used for logging purposes. Could be null or empty string. * @return The duration (in milliseconds) that the app is allow-listed for */ + @UserHandleAware @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long addToTemporaryAllowListForEvent(@NonNull String packageName, @AllowListEvent int event, @ReasonCode int reasonCode, @Nullable String reason) { @@ -576,6 +588,8 @@ public class PowerExemptionManager { return "ALLOWLISTED_PACKAGE"; case REASON_APPOP: return "APPOP"; + case REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD: + return "ACTIVITY_VISIBILITY_GRACE_PERIOD"; case REASON_GEOFENCING: return "GEOFENCING"; case REASON_PUSH_MESSAGING: @@ -626,6 +640,8 @@ public class PowerExemptionManager { return "EVENT_MMS"; case REASON_SHELL: return "SHELL"; + case REASON_MEDIA_SESSION_CALLBACK: + return "MEDIA_SESSION_CALLBACK"; default: return "(unknown:" + reasonCode + ")"; } diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 119dcb63770d..667fc60fb20b 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -2692,7 +2692,7 @@ public class DeviceIdleController extends SystemService void addPowerSaveTempAllowlistAppChecked(String packageName, long duration, int userId, @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException { - getContext().enforceCallingPermission( + getContext().enforceCallingOrSelfPermission( Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, "No permission to change device idle whitelist"); final int callingUid = Binder.getCallingUid(); @@ -2715,7 +2715,7 @@ public class DeviceIdleController extends SystemService void removePowerSaveTempAllowlistAppChecked(String packageName, int userId) throws RemoteException { - getContext().enforceCallingPermission( + getContext().enforceCallingOrSelfPermission( Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, "No permission to change device idle whitelist"); final int callingUid = Binder.getCallingUid(); diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 33f6e0651abc..58fc87476f2a 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -388,6 +388,8 @@ public class AlarmManagerService extends SystemService { @VisibleForTesting static final String KEY_MAX_INTERVAL = "max_interval"; @VisibleForTesting + static final String KEY_MIN_WINDOW = "min_window"; + @VisibleForTesting static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION = "allow_while_idle_whitelist_duration"; @VisibleForTesting @@ -428,11 +430,13 @@ public class AlarmManagerService extends SystemService { @VisibleForTesting static final String KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW = "allow_while_idle_compat_window"; - private static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps"; + @VisibleForTesting + static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps"; private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; private static final long DEFAULT_MAX_INTERVAL = 365 * INTERVAL_DAY; + private static final long DEFAULT_MIN_WINDOW = 10_000; private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000; private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000; private static final int DEFAULT_MAX_ALARMS_PER_UID = 500; @@ -475,6 +479,9 @@ public class AlarmManagerService extends SystemService { // Maximum alarm recurrence interval public long MAX_INTERVAL = DEFAULT_MAX_INTERVAL; + // Minimum window size for inexact alarms + public long MIN_WINDOW = DEFAULT_MIN_WINDOW; + // BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE. public long ALLOW_WHILE_IDLE_WHITELIST_DURATION = DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION; @@ -575,6 +582,9 @@ public class AlarmManagerService extends SystemService { ALLOW_WHILE_IDLE_QUOTA = 1; } break; + case KEY_MIN_WINDOW: + MIN_WINDOW = properties.getLong(KEY_MIN_WINDOW, DEFAULT_MIN_WINDOW); + break; case KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA: ALLOW_WHILE_IDLE_COMPAT_QUOTA = properties.getInt( KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, @@ -738,6 +748,11 @@ public class AlarmManagerService extends SystemService { TimeUtils.formatDuration(MAX_INTERVAL, pw); pw.println(); + pw.print(KEY_MIN_WINDOW); + pw.print("="); + TimeUtils.formatDuration(MIN_WINDOW, pw); + pw.println(); + pw.print(KEY_LISTENER_TIMEOUT); pw.print("="); TimeUtils.formatDuration(LISTENER_TIMEOUT, pw); @@ -1642,6 +1657,7 @@ public class AlarmManagerService extends SystemService { // Fix this window in place, so that as time approaches we don't collapse it. windowLength = maxElapsed - triggerElapsed; } else { + windowLength = Math.max(windowLength, mConstants.MIN_WINDOW); maxElapsed = triggerElapsed + windowLength; } synchronized (mLock) { @@ -1981,8 +1997,10 @@ public class AlarmManagerService extends SystemService { * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact, * allow-while-idle alarms. */ - boolean isExemptFromPermission(int uid) { - return (UserHandle.isSameApp(mSystemUiUid, uid) || mLocalDeviceIdleController == null + boolean isExemptFromExactAlarmPermission(int uid) { + return (UserHandle.isSameApp(mSystemUiUid, uid) + || UserHandle.isCore(uid) + || mLocalDeviceIdleController == null || mLocalDeviceIdleController.isAppOnWhitelist(UserHandle.getAppId(uid))); } @@ -2002,54 +2020,43 @@ public class AlarmManagerService extends SystemService { mAppOps.checkPackage(callingUid, callingPackage); final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0; + final boolean exact = (windowLength == AlarmManager.WINDOW_EXACT); + // make sure the caller is allowed to use the requested kind of alarm, and also + // decide what quota and broadcast options to use. Bundle idleOptions = null; - if (alarmClock != null || allowWhileIdle) { - // make sure the caller is allowed to use the requested kind of alarm, and also - // decide what broadcast options to use. + if (exact || allowWhileIdle) { final boolean needsPermission; - boolean lowQuota; + boolean lowerQuota; if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, callingPackage, UserHandle.getUserHandleForUid(callingUid))) { - if (windowLength != AlarmManager.WINDOW_EXACT) { - needsPermission = false; - lowQuota = true; - idleOptions = isExemptFromPermission(callingUid) ? mOptsWithFgs.toBundle() - : mOptsWithoutFgs.toBundle(); - } else if (alarmClock != null) { - needsPermission = true; - lowQuota = false; - idleOptions = mOptsWithFgs.toBundle(); - } else { - needsPermission = true; - lowQuota = false; - idleOptions = mOptsWithFgs.toBundle(); - } + needsPermission = exact; + lowerQuota = !exact; + idleOptions = exact ? mOptsWithFgs.toBundle() : mOptsWithoutFgs.toBundle(); } else { needsPermission = false; - lowQuota = allowWhileIdle; + lowerQuota = allowWhileIdle; idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null; } if (needsPermission && !canScheduleExactAlarms()) { - if (alarmClock == null && isExemptFromPermission(callingUid)) { - // If the app is on the full system allow-list (not except-idle), we still - // allow the alarms, but with a lower quota to keep pre-S compatibility. - lowQuota = true; - } else { + if (alarmClock != null || !isExemptFromExactAlarmPermission(callingUid)) { final String errorMessage = "Caller " + callingPackage + " needs to hold " + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set " - + ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock") - + " alarms."; + + "exact alarms."; if (mConstants.CRASH_NON_CLOCK_APPS) { throw new SecurityException(errorMessage); } else { Slog.wtf(TAG, errorMessage); - idleOptions = mOptsWithoutFgs.toBundle(); - lowQuota = allowWhileIdle; } } + // If the app is on the full system power allow-list (not except-idle), or we're + // in a soft failure mode, we still allow the alarms. + // We give temporary allowlist to allow-while-idle alarms but without FGS + // capability. Note that apps that are in the power allow-list do not need it. + idleOptions = allowWhileIdle ? mOptsWithoutFgs.toBundle() : null; + lowerQuota = allowWhileIdle; } - if (lowQuota) { + if (lowerQuota) { flags &= ~FLAG_ALLOW_WHILE_IDLE; flags |= FLAG_ALLOW_WHILE_IDLE_COMPAT; } @@ -2998,13 +3005,10 @@ public class AlarmManagerService extends SystemService { /** * Called when an app loses {@link Manifest.permission#SCHEDULE_EXACT_ALARM} to remove alarms * that the app is no longer eligible to use. - * TODO (b/179541791): Revisit and write tests once UX is final. + * TODO (b/179541791): Add revocation history to dumpsys. */ void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) { - if (UserHandle.isCore(uid) || uid == mSystemUiUid) { - return; - } - if (isExemptFromPermission(uid)) { + if (isExemptFromExactAlarmPermission(uid)) { return; } if (!CompatChanges.isChangeEnabled( @@ -3015,7 +3019,7 @@ public class AlarmManagerService extends SystemService { final Predicate<Alarm> whichAlarms = a -> (a.uid == uid && a.packageName.equals(packageName) - && ((a.flags & FLAG_ALLOW_WHILE_IDLE) != 0 || a.alarmClock != null)); + && a.windowLength == AlarmManager.WINDOW_EXACT); final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms); final boolean didRemove = !removed.isEmpty(); if (didRemove) { @@ -3873,6 +3877,7 @@ public class AlarmManagerService extends SystemService { return alarm.creatorUid; } + @VisibleForTesting class AlarmHandler extends Handler { public static final int ALARM_EVENT = 1; diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java index 0e442d09d5a5..e684b84748b1 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java @@ -134,6 +134,11 @@ public interface AlarmStore { void dumpProto(ProtoOutputStream pos, long nowElapsed); /** + * @return a name for this alarm store that can be used for debugging and tests. + */ + String getName(); + + /** * A functional interface used to update the alarm. Used to describe the update in * {@link #updateAlarmDeliveries(AlarmDeliveryCalculator)} */ diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java index e7edfb7b56b9..cb528ba2769a 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java @@ -27,6 +27,7 @@ import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.StatLogger; import java.text.SimpleDateFormat; @@ -40,6 +41,8 @@ import java.util.function.Predicate; * This keeps the alarms in batches, which are sorted on the start time of their delivery window. */ public class BatchingAlarmStore implements AlarmStore { + @VisibleForTesting + static final String TAG = BatchingAlarmStore.class.getSimpleName(); private final ArrayList<Batch> mAlarmBatches = new ArrayList<>(); private int mSize; @@ -49,7 +52,7 @@ public class BatchingAlarmStore implements AlarmStore { int REBATCH_ALL_ALARMS = 0; } - final StatLogger mStatLogger = new StatLogger("BatchingAlarmStore stats", new String[]{ + final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{ "REBATCH_ALL_ALARMS", }); @@ -211,6 +214,11 @@ public class BatchingAlarmStore implements AlarmStore { } } + @Override + public String getName() { + return TAG; + } + private void insertAndBatchAlarm(Alarm alarm) { final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1 : attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed()); diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java index 8ca14463a3b5..c37d2c36b068 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java @@ -25,6 +25,7 @@ import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.StatLogger; import java.text.SimpleDateFormat; @@ -38,6 +39,8 @@ import java.util.function.Predicate; * This keeps the alarms in a sorted list, and only batches them at the time of delivery. */ public class LazyAlarmStore implements AlarmStore { + @VisibleForTesting + static final String TAG = LazyAlarmStore.class.getSimpleName(); private final ArrayList<Alarm> mAlarms = new ArrayList<>(); private Runnable mOnAlarmClockRemoved; @@ -47,7 +50,7 @@ public class LazyAlarmStore implements AlarmStore { int GET_NEXT_WAKEUP_DELIVERY_TIME = 1; } - final StatLogger mStatLogger = new StatLogger("LazyAlarmStore stats", new String[]{ + final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{ "GET_NEXT_DELIVERY_TIME", "GET_NEXT_WAKEUP_DELIVERY_TIME", }); @@ -214,4 +217,9 @@ public class LazyAlarmStore implements AlarmStore { a.dumpDebug(pos, AlarmManagerServiceDumpProto.PENDING_ALARMS, nowElapsed); } } + + @Override + public String getName() { + return TAG; + } } 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 b70e68b739d8..2f3ac225a190 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 @@ -813,12 +813,21 @@ public final class QuotaController extends StateController { // 1. it's already running (already executing expedited jobs should be allowed to finish) // 2. the app is currently in the foreground // 3. the app overall is within its quota + // 4. It's on the temp allowlist (or within the grace period) if (isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) { return true; } + final long tempAllowlistGracePeriodEndElapsed = + mTempAllowlistGraceCache.get(jobStatus.getSourceUid()); + final boolean hasTempAllowlistExemption = mTempAllowlistCache.get(jobStatus.getSourceUid()) + || sElapsedRealtimeClock.millis() < tempAllowlistGracePeriodEndElapsed; + if (hasTempAllowlistExemption) { + return true; + } + Timer ejTimer = mEJPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); - // Any already executing expedited jbos should be allowed to finish. + // Any already executing expedited jobs should be allowed to finish. if (ejTimer != null && ejTimer.isRunning(jobStatus)) { return true; } diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 3d129d8548eb..20ce13322fd2 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -135,6 +135,10 @@ java_sdk_library { ":updatable-media-srcs", ], + api_lint: { + enabled: false, + }, + libs: [ "framework_media_annotation", ], diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt index 6158e2ece55f..1d912ebc71fa 100644 --- a/apex/media/framework/api/system-current.txt +++ b/apex/media/framework/api/system-current.txt @@ -3,38 +3,19 @@ package android.media { public final class MediaTranscodeManager { method @Nullable public android.media.MediaTranscodeManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener); - field public static final int PRIORITY_REALTIME = 1; // 0x1 - field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1 } @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener { method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingSession); } - public static final class MediaTranscodeManager.TranscodingRequest { + public abstract static class MediaTranscodeManager.TranscodingRequest { method public int getClientPid(); method public int getClientUid(); method @Nullable public android.os.ParcelFileDescriptor getDestinationFileDescriptor(); method @NonNull public android.net.Uri getDestinationUri(); - method public int getPriority(); method @Nullable public android.os.ParcelFileDescriptor getSourceFileDescriptor(); method @NonNull public android.net.Uri getSourceUri(); - method public int getType(); - method @Nullable public android.media.MediaFormat getVideoTrackFormat(); - } - - public static final class MediaTranscodeManager.TranscodingRequest.Builder { - ctor public MediaTranscodeManager.TranscodingRequest.Builder(); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build(); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat); } public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver { @@ -71,5 +52,18 @@ package android.media { method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingSession, @IntRange(from=0, to=100) int); } + public static final class MediaTranscodeManager.VideoTranscodingRequest extends android.media.MediaTranscodeManager.TranscodingRequest { + method @NonNull public android.media.MediaFormat getVideoTrackFormat(); + } + + public static final class MediaTranscodeManager.VideoTranscodingRequest.Builder { + ctor public MediaTranscodeManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat); + method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest build(); + method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientPid(int); + method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientUid(int); + method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(android.os.ParcelFileDescriptor); + method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(android.os.ParcelFileDescriptor); + } + } diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java index 906071fe4c7b..3f30d3ea880e 100644 --- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java +++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java @@ -19,11 +19,14 @@ package android.media; import android.annotation.NonNull; import android.content.ContentResolver; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; +import com.android.modules.annotation.MinSdk; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -77,6 +80,7 @@ import java.util.Set; There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform, application will only need to specify individual types they supported. */ +@MinSdk(Build.VERSION_CODES.S) public final class ApplicationMediaCapabilities implements Parcelable { private static final String TAG = "ApplicationMediaCapabilities"; diff --git a/apex/media/framework/java/android/media/MediaFeature.java b/apex/media/framework/java/android/media/MediaFeature.java index 0e461888a0e3..8d1b159cd70b 100644 --- a/apex/media/framework/java/android/media/MediaFeature.java +++ b/apex/media/framework/java/android/media/MediaFeature.java @@ -17,6 +17,9 @@ package android.media; import android.annotation.StringDef; +import android.os.Build; + +import com.android.modules.annotation.MinSdk; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -24,6 +27,7 @@ import java.lang.annotation.RetentionPolicy; /** * MediaFeature defines various media features, e.g. hdr type. */ +@MinSdk(Build.VERSION_CODES.S) public final class MediaFeature { /** * Defines tye type of HDR(high dynamic range) video. diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java index 93328355026e..de2924e160b6 100644 --- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java +++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java @@ -21,7 +21,9 @@ import android.annotation.SystemApi; import android.annotation.SystemApi.Client; import android.app.SystemServiceRegistry; import android.content.Context; +import android.os.Build; +import com.android.modules.annotation.MinSdk; import com.android.modules.utils.build.SdkLevel; /** @@ -29,6 +31,7 @@ import com.android.modules.utils.build.SdkLevel; * * @hide */ +@MinSdk(Build.VERSION_CODES.S) @SystemApi(client = Client.MODULE_LIBRARIES) public class MediaFrameworkInitializer { private MediaFrameworkInitializer() { diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java index 30d1896a23ac..79e0d58cf495 100644 --- a/apex/media/framework/java/android/media/MediaTranscodeManager.java +++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java @@ -26,6 +26,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.net.Uri; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -34,6 +35,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.annotation.MinSdk; import java.io.FileNotFoundException; import java.lang.annotation.Retention; @@ -48,27 +50,23 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - MediaTranscodeManager provides an interface to the system's media transcoding service and can be - used to transcode media files, e.g. transcoding a video from HEVC to AVC. + Android 12 introduces Compatible media transcoding feature. See + <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding"> + Compatible media transcoding</a>. MediaTranscodeManager provides an interface to the system's media + transcoding service and can be used to transcode media files, e.g. transcoding a video from HEVC to + AVC. <h3>Transcoding Types</h3> <h4>Video Transcoding</h4> - When transcoding a video file, the video file could be of any of the following types: - <ul> - <li> Video file with single video track. </li> - <li> Video file with multiple video track. </li> - <li> Video file with multiple video tracks and audio tracks. </li> - <li> Video file with video/audio tracks and metadata track. Note that metadata track will be passed - through only if it could be recognized by {@link MediaExtractor}. - TODO(hkuang): Finalize the metadata track behavior. </li> - </ul> + When transcoding a video file, the video track will be transcoded based on the desired track format + and the audio track will be pass through without any modification. <p class=note> - Note that currently only support transcoding video file in mp4 format. + Note that currently only support transcoding video file in mp4 format and with single video track. <h3>Transcoding Request</h3> <p> To transcode a media file, first create a {@link TranscodingRequest} through its builder class - {@link TranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through + {@link VideoTranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through {@link MediaTranscodeManager#enqueueRequest( TranscodingRequest, Executor, OnTranscodingFinishedListener)} TranscodeRequest are processed based on client process's priority and request priority. When a @@ -80,25 +78,12 @@ import java.util.concurrent.Executors; Here is an example where <code>Builder</code> is used to specify all parameters <pre class=prettyprint> - TranscodingRequest request = - new TranscodingRequest.Builder() - .setSourceUri(srcUri) - .setDestinationUri(dstUri) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) - .setPriority(REALTIME) - .setVideoTrackFormat(videoFormat) - .build(); + VideoTranscodingRequest request = + new VideoTranscodingRequest.Builder(srcUri, dstUri, videoFormat).build(); }</pre> - - TODO(hkuang): Add architecture diagram showing the transcoding service and api. - TODO(hkuang): Add sample code when API is settled. - TODO(hkuang): Clarify whether multiple video tracks is supported or not. - TODO(hkuang): Clarify whether image/audio transcoding is supported or not. - TODO(hkuang): Clarify what will happen if there is unrecognized track in the source. - TODO(hkuang): Clarify whether supports scaling. - TODO(hkuang): Clarify whether supports framerate conversion. @hide */ +@MinSdk(Build.VERSION_CODES.S) @SystemApi public final class MediaTranscodeManager { private static final String TAG = "MediaTranscodeManager"; @@ -113,68 +98,6 @@ public final class MediaTranscodeManager { private static final float BPP = 0.25f; /** - * Default transcoding type. - * @hide - */ - public static final int TRANSCODING_TYPE_UNKNOWN = 0; - - /** - * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video file. - * <p>Note that currently only support transcoding video file in mp4 format. - */ - public static final int TRANSCODING_TYPE_VIDEO = 1; - - /** - * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image file. - * @hide - */ - public static final int TRANSCODING_TYPE_IMAGE = 2; - - /** @hide */ - @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = { - TRANSCODING_TYPE_UNKNOWN, - TRANSCODING_TYPE_VIDEO, - TRANSCODING_TYPE_IMAGE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TranscodingType {} - - /** - * Default value. - * @hide - */ - public static final int PRIORITY_UNKNOWN = 0; - /** - * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the - * client wants the transcoding result as soon as possible. - * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve - * performance penalty due to resource reallocation to prioritize the sessions with higher - * priority. - * TODO(hkuang): Add more description of this when priority is finalized. - */ - public static final int PRIORITY_REALTIME = 1; - - /** - * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not need - * the transcoding result as soon as possible. - * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set to - * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept - * delay of the transcoding result. - * @hide - * TODO(hkuang): Add more description of this when priority is finalized. - */ - public static final int PRIORITY_OFFLINE = 2; - - /** @hide */ - @IntDef(prefix = {"PRIORITY_"}, value = { - PRIORITY_UNKNOWN, - PRIORITY_REALTIME, - PRIORITY_OFFLINE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TranscodingPriority {} - - /** * Listener that gets notified when a transcoding operation has finished. * This listener gets notified regardless of how the operation finished. It is up to the * listener implementation to check the result and take appropriate action. @@ -500,7 +423,79 @@ public final class MediaTranscodeManager { } } - public static final class TranscodingRequest { + /** + * Abstract base class for all the TranscodingRequest. + * <p> TranscodingRequest encapsulates the desired configuration for the transcoding. + */ + public abstract static class TranscodingRequest { + /** + * + * Default transcoding type. + * @hide + */ + public static final int TRANSCODING_TYPE_UNKNOWN = 0; + + /** + * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video. + * <p>Note that currently only support transcoding video file in mp4 format. + * @hide + */ + public static final int TRANSCODING_TYPE_VIDEO = 1; + + /** + * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image. + * @hide + */ + public static final int TRANSCODING_TYPE_IMAGE = 2; + + /** @hide */ + @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = { + TRANSCODING_TYPE_UNKNOWN, + TRANSCODING_TYPE_VIDEO, + TRANSCODING_TYPE_IMAGE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TranscodingType {} + + /** + * Default value. + * + * @hide + */ + public static final int PRIORITY_UNKNOWN = 0; + /** + * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the + * client wants the transcoding result as soon as possible. + * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve + * performance penalty due to resource reallocation to prioritize the sessions with higher + * priority. + * + * @hide + */ + public static final int PRIORITY_REALTIME = 1; + + /** + * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not + * need the transcoding result as soon as possible. + * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set + * to + * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept + * delay of the transcoding result. + * + * @hide + * + */ + public static final int PRIORITY_OFFLINE = 2; + + /** @hide */ + @IntDef(prefix = {"PRIORITY_"}, value = { + PRIORITY_UNKNOWN, + PRIORITY_REALTIME, + PRIORITY_OFFLINE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TranscodingPriority {} + /** Uri of the source media file. */ private @NonNull Uri mSourceUri; @@ -534,22 +529,6 @@ public final class MediaTranscodeManager { private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN; /** - * Desired output video format of the destination file. - * <p> If this is null, source file's video track will be passed through and copied to the - * destination file. - * <p> - */ - private @Nullable MediaFormat mVideoTrackFormat = null; - - /** - * Desired output audio format of the destination file. - * <p> If this is null, source file's audio track will be passed through and copied to the - * destination file. - * @hide - */ - private @Nullable MediaFormat mAudioTrackFormat = null; - - /** * Desired image format for the destination file. * <p> If this is null, source file's image track will be passed through and copied to the * destination file. @@ -560,6 +539,12 @@ public final class MediaTranscodeManager { @VisibleForTesting private TranscodingTestConfig mTestConfig = null; + /** + * Prevent public constructor access. + */ + /* package private */ TranscodingRequest() { + } + private TranscodingRequest(Builder b) { mSourceUri = b.mSourceUri; mSourceFileDescriptor = b.mSourceFileDescriptor; @@ -569,13 +554,13 @@ public final class MediaTranscodeManager { mClientPid = b.mClientPid; mPriority = b.mPriority; mType = b.mType; - mVideoTrackFormat = b.mVideoTrackFormat; - mAudioTrackFormat = b.mAudioTrackFormat; - mImageFormat = b.mImageFormat; mTestConfig = b.mTestConfig; } - /** Return the type of the transcoding. */ + /** + * Return the type of the transcoding. + * @hide + */ @TranscodingType public int getType() { return mType; @@ -621,22 +606,16 @@ public final class MediaTranscodeManager { return mDestinationFileDescriptor; } - /** Return priority of the transcoding. */ + /** + * Return priority of the transcoding. + * @hide + */ @TranscodingPriority public int getPriority() { return mPriority; } /** - * Return the video track format of the transcoding. - * This will be null is the transcoding is not for video transcoding. - */ - @Nullable - public MediaFormat getVideoTrackFormat() { - return mVideoTrackFormat; - } - - /** * Return TestConfig of the transcoding. * @hide */ @@ -645,6 +624,8 @@ public final class MediaTranscodeManager { return mTestConfig; } + abstract void writeFormatToParcel(TranscodingRequestParcel parcel); + /* Writes the TranscodingRequest to a parcel. */ private TranscodingRequestParcel writeToParcel(@NonNull Context context) { TranscodingRequestParcel parcel = new TranscodingRequestParcel(); @@ -668,7 +649,7 @@ public final class MediaTranscodeManager { } parcel.clientPackageName = packageName; } - parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat); + writeFormatToParcel(parcel); if (mTestConfig != null) { parcel.isForTesting = true; parcel.testConfig = mTestConfig; @@ -676,71 +657,12 @@ public final class MediaTranscodeManager { return parcel; } - /* Converts the MediaFormat to TranscodingVideoTrackFormat. */ - private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) { - if (format == null) { - throw new IllegalArgumentException("Invalid MediaFormat"); - } - - TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat(); - - if (format.containsKey(MediaFormat.KEY_MIME)) { - String mime = format.getString(MediaFormat.KEY_MIME); - if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) { - trackFormat.codecType = TranscodingVideoCodecType.kAvc; - } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) { - trackFormat.codecType = TranscodingVideoCodecType.kHevc; - } else { - throw new UnsupportedOperationException("Only support transcode to avc/hevc"); - } - } - - if (format.containsKey(MediaFormat.KEY_BIT_RATE)) { - int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE); - if (bitrateBps <= 0) { - throw new IllegalArgumentException("Bitrate must be larger than 0"); - } - trackFormat.bitrateBps = bitrateBps; - } - - if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey( - MediaFormat.KEY_HEIGHT)) { - int width = format.getInteger(MediaFormat.KEY_WIDTH); - int height = format.getInteger(MediaFormat.KEY_HEIGHT); - if (width <= 0 || height <= 0) { - throw new IllegalArgumentException("Width and height must be larger than 0"); - } - // TODO(hkuang): Validate the aspect ratio after adding scaling. - trackFormat.width = width; - trackFormat.height = height; - } - - if (format.containsKey(MediaFormat.KEY_PROFILE)) { - int profile = format.getInteger(MediaFormat.KEY_PROFILE); - if (profile <= 0) { - throw new IllegalArgumentException("Invalid codec profile"); - } - // TODO(hkuang): Validate the profile according to codec type. - trackFormat.profile = profile; - } - - if (format.containsKey(MediaFormat.KEY_LEVEL)) { - int level = format.getInteger(MediaFormat.KEY_LEVEL); - if (level <= 0) { - throw new IllegalArgumentException("Invalid codec level"); - } - // TODO(hkuang): Validate the level according to codec type. - trackFormat.level = level; - } - - return trackFormat; - } - /** - * Builder class for {@link TranscodingRequest} objects. - * Use this class to configure and create a <code>TranscodingRequest</code> instance. + * Builder to build a {@link TranscodingRequest} object. + * + * @param <T> The subclass to be built. */ - public static final class Builder { + abstract static class Builder<T extends Builder<T>> { private @NonNull Uri mSourceUri; private @NonNull Uri mDestinationUri; private @Nullable ParcelFileDescriptor mSourceFileDescriptor = null; @@ -749,80 +671,68 @@ public final class MediaTranscodeManager { private int mClientPid = -1; private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN; private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN; - private @Nullable MediaFormat mVideoTrackFormat; - private @Nullable MediaFormat mAudioTrackFormat; - private @Nullable MediaFormat mImageFormat; private TranscodingTestConfig mTestConfig; + abstract T self(); + /** - * Specifies the uri of source media file. + * Creates a builder for building {@link TranscodingRequest}s. * * Client must set the source Uri. If client also provides the source fileDescriptor * through is provided by {@link #setSourceFileDescriptor(ParcelFileDescriptor)}, * TranscodingSession will use the fd instead of calling back to the client to open the * sourceUri. + * + * + * @param type The transcoding type. * @param sourceUri Content uri for the source media file. - * @return The same builder instance. - * @throws IllegalArgumentException if Uri is null or empty. + * @param destinationUri Content uri for the destination media file. + * */ - @NonNull - public Builder setSourceUri(@NonNull Uri sourceUri) { + private Builder(@TranscodingType int type, @NonNull Uri sourceUri, + @NonNull Uri destinationUri) { + mType = type; + if (sourceUri == null || Uri.EMPTY.equals(sourceUri)) { throw new IllegalArgumentException( "You must specify a non-empty source Uri."); } mSourceUri = sourceUri; - return this; + + if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) { + throw new IllegalArgumentException( + "You must specify a non-empty destination Uri."); + } + mDestinationUri = destinationUri; } /** * Specifies the fileDescriptor opened from the source media file. * * This call is optional. If the source fileDescriptor is provided, TranscodingSession - * will use it directly instead of opening the uri from {@link #setSourceUri(Uri)}. It - * is client's responsibility to make sure the fileDescriptor is opened from the source - * uri. + * will use it directly instead of opening the uri from {@link #Builder(int, Uri, Uri)}. + * It is client's responsibility to make sure the fileDescriptor is opened from the + * source uri. * @param fileDescriptor a {@link ParcelFileDescriptor} opened from source media file. * @return The same builder instance. * @throws IllegalArgumentException if fileDescriptor is invalid. */ @NonNull - public Builder setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) { + public T setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) { if (fileDescriptor == null || fileDescriptor.getFd() < 0) { throw new IllegalArgumentException( "Invalid source descriptor."); } mSourceFileDescriptor = fileDescriptor; - return this; - } - - /** - * Specifies the uri of the destination media file. - * - * Client must set the destination Uri. If client also provides the destination - * fileDescriptor through {@link #setDestinationFileDescriptor(ParcelFileDescriptor)}, - * TranscodingSession will use the fd instead of calling back to the client to open the - * destinationUri. - * @param destinationUri Content uri for the destination media file. - * @return The same builder instance. - * @throws IllegalArgumentException if Uri is null or empty. - */ - @NonNull - public Builder setDestinationUri(@NonNull Uri destinationUri) { - if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) { - throw new IllegalArgumentException( - "You must specify a non-empty destination Uri."); - } - mDestinationUri = destinationUri; - return this; + return self(); } /** * Specifies the fileDescriptor opened from the destination media file. * * This call is optional. If the destination fileDescriptor is provided, - * TranscodingSession will use it directly instead of opening the uri from - * {@link #setDestinationUri(Uri)} upon transcoding starts. It is client's + * TranscodingSession will use it directly instead of opening the source uri from + * {@link #Builder(int, Uri, Uri)} upon transcoding starts. It is client's * responsibility to make sure the fileDescriptor is opened from the destination uri. * @param fileDescriptor a {@link ParcelFileDescriptor} opened from destination media * file. @@ -830,46 +740,54 @@ public final class MediaTranscodeManager { * @throws IllegalArgumentException if fileDescriptor is invalid. */ @NonNull - public Builder setDestinationFileDescriptor( + public T setDestinationFileDescriptor( @NonNull ParcelFileDescriptor fileDescriptor) { if (fileDescriptor == null || fileDescriptor.getFd() < 0) { throw new IllegalArgumentException( "Invalid destination descriptor."); } mDestinationFileDescriptor = fileDescriptor; - return this; + return self(); } /** * Specify the UID of the client that this request is for. + * <p> + * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the + * pid. Note that the permission check happens on the service side upon starting the + * transcoding. If the client does not have the permission, the transcoding will fail. + * * @param uid client Uid. * @return The same builder instance. * @throws IllegalArgumentException if uid is invalid. - * TODO(hkuang): Check the permission if it is allowed. */ @NonNull - public Builder setClientUid(int uid) { + public T setClientUid(int uid) { if (uid < 0) { throw new IllegalArgumentException("Invalid Uid"); } mClientUid = uid; - return this; + return self(); } /** - * Specify the PID of the client that this request is for. + * Specify the pid of the client that this request is for. + * <p> + * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the + * pid. Note that the permission check happens on the service side upon starting the + * transcoding. If the client does not have the permission, the transcoding will fail. + * * @param pid client Pid. * @return The same builder instance. * @throws IllegalArgumentException if pid is invalid. - * TODO(hkuang): Check the permission if it is allowed. */ @NonNull - public Builder setClientPid(int pid) { + public T setClientPid(int pid) { if (pid < 0) { throw new IllegalArgumentException("Invalid pid"); } mClientPid = pid; - return this; + return self(); } /** @@ -878,64 +796,15 @@ public final class MediaTranscodeManager { * @param priority Must be one of the {@code PRIORITY_*} * @return The same builder instance. * @throws IllegalArgumentException if flags is invalid. + * @hide */ @NonNull - public Builder setPriority(@TranscodingPriority int priority) { + public T setPriority(@TranscodingPriority int priority) { if (priority != PRIORITY_OFFLINE && priority != PRIORITY_REALTIME) { throw new IllegalArgumentException("Invalid priority: " + priority); } mPriority = priority; - return this; - } - - /** - * Specifies the type of transcoding. - * <p> Clients must provide the source and destination that corresponds to the - * transcoding type. - * - * @param type Must be one of the {@code TRANSCODING_TYPE_*} - * @return The same builder instance. - * @throws IllegalArgumentException if flags is invalid. - */ - @NonNull - public Builder setType(@TranscodingType int type) { - if (type != TRANSCODING_TYPE_VIDEO && type != TRANSCODING_TYPE_IMAGE) { - throw new IllegalArgumentException("Invalid transcoding type"); - } - mType = type; - return this; - } - - /** - * Specifies the desired video track format in the destination media file. - * <p>Client could only specify the settings that matters to them, e.g. codec format or - * bitrate. And by default, transcoding will preserve the original video's - * settings(bitrate, framerate, resolution) if not provided. - * <p>Note that some settings may silently fail to apply if the device does not - * support them. - * TODO(hkuang): Add MediaTranscodeUtil to help client generate transcoding setting. - * TODO(hkuang): Add MediaTranscodeUtil to check if the setting is valid. - * - * @param videoFormat MediaFormat containing the settings that client wants override in - * the original video's video track. - * @return The same builder instance. - * @throws IllegalArgumentException if videoFormat is invalid. - */ - @NonNull - public Builder setVideoTrackFormat(@NonNull MediaFormat videoFormat) { - if (videoFormat == null) { - throw new IllegalArgumentException("videoFormat must not be null"); - } - - // Check if the MediaFormat is for video by looking at the MIME type. - String mime = videoFormat.containsKey(MediaFormat.KEY_MIME) - ? videoFormat.getString(MediaFormat.KEY_MIME) : null; - if (mime == null || !mime.startsWith("video/")) { - throw new IllegalArgumentException("Invalid video format: wrong mime type"); - } - - mVideoTrackFormat = videoFormat; - return this; + return self(); } /** @@ -946,44 +815,9 @@ public final class MediaTranscodeManager { */ @VisibleForTesting @NonNull - public Builder setTestConfig(@NonNull TranscodingTestConfig config) { + public T setTestConfig(@NonNull TranscodingTestConfig config) { mTestConfig = config; - return this; - } - - /** - * @return a new {@link TranscodingRequest} instance successfully initialized with all - * the parameters set on this <code>Builder</code>. - * @throws UnsupportedOperationException if the parameters set on the - * <code>Builder</code> were incompatible, or if they are not supported by the - * device. - */ - @NonNull - public TranscodingRequest build() { - if (mSourceUri == null) { - throw new UnsupportedOperationException("Source URI must not be null"); - } - - if (mDestinationUri == null) { - throw new UnsupportedOperationException("Destination URI must not be null"); - } - - if (mPriority == PRIORITY_UNKNOWN) { - throw new UnsupportedOperationException("Must specify transcoding priority"); - } - - // Only support video transcoding now. - if (mType != TRANSCODING_TYPE_VIDEO) { - throw new UnsupportedOperationException("Only supports video transcoding now"); - } - - // Must provide video track format for video transcoding. - if (mType == TRANSCODING_TYPE_VIDEO && mVideoTrackFormat == null) { - throw new UnsupportedOperationException( - "Must provide video track format for video transcoding"); - } - - return new TranscodingRequest(this); + return self(); } } @@ -1198,6 +1032,206 @@ public final class MediaTranscodeManager { } /** + * VideoTranscodingRequest encapsulates the configuration for transcoding a video. + */ + public static final class VideoTranscodingRequest extends TranscodingRequest { + /** + * Desired output video format of the destination file. + * <p> If this is null, source file's video track will be passed through and copied to the + * destination file. + */ + private @Nullable MediaFormat mVideoTrackFormat = null; + + /** + * Desired output audio format of the destination file. + * <p> If this is null, source file's audio track will be passed through and copied to the + * destination file. + */ + private @Nullable MediaFormat mAudioTrackFormat = null; + + private VideoTranscodingRequest(VideoTranscodingRequest.Builder builder) { + super(builder); + mVideoTrackFormat = builder.mVideoTrackFormat; + mAudioTrackFormat = builder.mAudioTrackFormat; + } + + /** + * Return the video track format of the transcoding. + * This will be null if client has not specified the video track format. + */ + @NonNull + public MediaFormat getVideoTrackFormat() { + return mVideoTrackFormat; + } + + @Override + void writeFormatToParcel(TranscodingRequestParcel parcel) { + parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat); + } + + /* Converts the MediaFormat to TranscodingVideoTrackFormat. */ + private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) { + if (format == null) { + throw new IllegalArgumentException("Invalid MediaFormat"); + } + + TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat(); + + if (format.containsKey(MediaFormat.KEY_MIME)) { + String mime = format.getString(MediaFormat.KEY_MIME); + if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) { + trackFormat.codecType = TranscodingVideoCodecType.kAvc; + } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) { + trackFormat.codecType = TranscodingVideoCodecType.kHevc; + } else { + throw new UnsupportedOperationException("Only support transcode to avc/hevc"); + } + } + + if (format.containsKey(MediaFormat.KEY_BIT_RATE)) { + int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE); + if (bitrateBps <= 0) { + throw new IllegalArgumentException("Bitrate must be larger than 0"); + } + trackFormat.bitrateBps = bitrateBps; + } + + if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey( + MediaFormat.KEY_HEIGHT)) { + int width = format.getInteger(MediaFormat.KEY_WIDTH); + int height = format.getInteger(MediaFormat.KEY_HEIGHT); + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("Width and height must be larger than 0"); + } + // TODO: Validate the aspect ratio after adding scaling. + trackFormat.width = width; + trackFormat.height = height; + } + + if (format.containsKey(MediaFormat.KEY_PROFILE)) { + int profile = format.getInteger(MediaFormat.KEY_PROFILE); + if (profile <= 0) { + throw new IllegalArgumentException("Invalid codec profile"); + } + // TODO: Validate the profile according to codec type. + trackFormat.profile = profile; + } + + if (format.containsKey(MediaFormat.KEY_LEVEL)) { + int level = format.getInteger(MediaFormat.KEY_LEVEL); + if (level <= 0) { + throw new IllegalArgumentException("Invalid codec level"); + } + // TODO: Validate the level according to codec type. + trackFormat.level = level; + } + + return trackFormat; + } + + /** + * Builder class for {@link VideoTranscodingRequest}. + */ + public static final class Builder extends + TranscodingRequest.Builder<VideoTranscodingRequest.Builder> { + /** + * Desired output video format of the destination file. + * <p> If this is null, source file's video track will be passed through and + * copied to the destination file. + */ + private @Nullable MediaFormat mVideoTrackFormat = null; + + /** + * Desired output audio format of the destination file. + * <p> If this is null, source file's audio track will be passed through and copied + * to the destination file. + */ + private @Nullable MediaFormat mAudioTrackFormat = null; + + /** + * Creates a builder for building {@link VideoTranscodingRequest}s. + * + * <p> Client could only specify the settings that matters to them, e.g. codec format or + * bitrate. And by default, transcoding will preserve the original video's settings + * (bitrate, framerate, resolution) if not provided. + * <p>Note that some settings may silently fail to apply if the device does not support + * them. + * @param sourceUri Content uri for the source media file. + * @param destinationUri Content uri for the destination media file. + * @param videoFormat MediaFormat containing the settings that client wants override in + * the original video's video track. + * @throws IllegalArgumentException if videoFormat is invalid. + */ + public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri, + @NonNull MediaFormat videoFormat) { + super(TRANSCODING_TYPE_VIDEO, sourceUri, destinationUri); + setVideoTrackFormat(videoFormat); + } + + @Override + @NonNull + public Builder setClientUid(int uid) { + super.setClientUid(uid); + return self(); + } + + @Override + @NonNull + public Builder setClientPid(int pid) { + super.setClientPid(pid); + return self(); + } + + @Override + @NonNull + public Builder setSourceFileDescriptor(ParcelFileDescriptor fd) { + super.setSourceFileDescriptor(fd); + return self(); + } + + @Override + @NonNull + public Builder setDestinationFileDescriptor(ParcelFileDescriptor fd) { + super.setDestinationFileDescriptor(fd); + return self(); + } + + private void setVideoTrackFormat(@NonNull MediaFormat videoFormat) { + if (videoFormat == null) { + throw new IllegalArgumentException("videoFormat must not be null"); + } + + // Check if the MediaFormat is for video by looking at the MIME type. + String mime = videoFormat.containsKey(MediaFormat.KEY_MIME) + ? videoFormat.getString(MediaFormat.KEY_MIME) : null; + if (mime == null || !mime.startsWith("video/")) { + throw new IllegalArgumentException("Invalid video format: wrong mime type"); + } + + mVideoTrackFormat = videoFormat; + } + + /** + * @return a new {@link TranscodingRequest} instance successfully initialized + * with all the parameters set on this <code>Builder</code>. + * @throws UnsupportedOperationException if the parameters set on the + * <code>Builder</code> were incompatible, or + * if they are not supported by the + * device. + */ + @NonNull + public VideoTranscodingRequest build() { + return new VideoTranscodingRequest(this); + } + + @Override + VideoTranscodingRequest.Builder self() { + return this; + } + } + } + + /** * Handle to an enqueued transcoding operation. An instance of this class represents a single * enqueued transcoding operation. The caller can use that instance to query the status or * progress, and to get the result once the operation has completed. diff --git a/api/Android.bp b/api/Android.bp index 1fdf1771bb13..4baf7c1d5836 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -351,6 +351,7 @@ genrule { genrule { name: "services-system-server-current.txt", srcs: [ + ":service-media-s{.system-server.api.txt}", ":service-permission{.system-server.api.txt}", ":non-updatable-system-server-current.txt", ], @@ -374,6 +375,7 @@ genrule { genrule { name: "services-system-server-removed.txt", srcs: [ + ":service-media-s{.system-server.removed-api.txt}", ":service-permission{.system-server.removed-api.txt}", ":non-updatable-system-server-removed.txt", ], diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp index 4e5b3bac5713..0eff83c99282 100644 --- a/cmds/app_process/Android.bp +++ b/cmds/app_process/Android.bp @@ -22,13 +22,9 @@ cc_binary { multilib: { lib32: { - // TODO(b/142944043): Remove version script when libsigchain is a DSO. - version_script: "version-script32.txt", suffix: "32", }, lib64: { - // TODO(b/142944043): Remove version script when libsigchain is a DSO. - version_script: "version-script64.txt", suffix: "64", }, }, @@ -43,6 +39,13 @@ cc_binary { "libhidlbase", "liblog", "libnativeloader", + + // Even though app_process doesn't call into libsigchain, we need to + // make sure it's in the DT list of app_process, as we want all code + // in app_process and the libraries it loads to find libsigchain + // symbols before libc symbols. + "libsigchain", + "libutils", // This is a list of libraries that need to be included in order to avoid @@ -52,8 +55,6 @@ cc_binary { "libwilhelm", ], - whole_static_libs: ["libsigchain"], - compile_multilib: "both", cflags: [ diff --git a/cmds/app_process/version-script32.txt b/cmds/app_process/version-script32.txt deleted file mode 100644 index 70810e0b7173..000000000000 --- a/cmds/app_process/version-script32.txt +++ /dev/null @@ -1,15 +0,0 @@ -{ -global: - EnsureFrontOfChain; - AddSpecialSignalHandlerFn; - RemoveSpecialSignalHandlerFn; - SkipAddSignalHandler; - bsd_signal; - sigaction; - sigaction64; - signal; - sigprocmask; - sigprocmask64; -local: - *; -}; diff --git a/cmds/app_process/version-script64.txt b/cmds/app_process/version-script64.txt deleted file mode 100644 index 7bcd76b50f87..000000000000 --- a/cmds/app_process/version-script64.txt +++ /dev/null @@ -1,14 +0,0 @@ -{ -global: - EnsureFrontOfChain; - AddSpecialSignalHandlerFn; - RemoveSpecialSignalHandlerFn; - SkipAddSignalHandler; - sigaction; - sigaction64; - signal; - sigprocmask; - sigprocmask64; -local: - *; -}; diff --git a/core/api/current.txt b/core/api/current.txt index bca2e92d43c3..2b5196b3b5f7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -99,6 +99,7 @@ package android { field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE"; field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS"; field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE"; + field public static final String MANAGE_MEDIA = "android.permission.MANAGE_MEDIA"; field public static final String MANAGE_ONGOING_CALLS = "android.permission.MANAGE_ONGOING_CALLS"; field public static final String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS"; field public static final String MASTER_CLEAR = "android.permission.MASTER_CLEAR"; @@ -4351,8 +4352,8 @@ package android.app { method public void set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler); method @RequiresPermission(android.Manifest.permission.SCHEDULE_EXACT_ALARM) public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent); method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent); - method public void setExact(int, long, android.app.PendingIntent); - method public void setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler); + method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, android.app.PendingIntent); + method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler); method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent); method public void setInexactRepeating(int, long, long, android.app.PendingIntent); method public void setRepeating(int, long, long, android.app.PendingIntent); @@ -40377,6 +40378,7 @@ package android.telephony { field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool"; field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string"; field public static final String KEY_CARRIER_NR_AVAILABILITY_INT = "carrier_nr_availability_int"; + field public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = "carrier_provisions_wifi_merged_networks_bool"; field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool"; field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string"; field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 5b618358d464..0eb5553e11fa 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1795,6 +1795,7 @@ package android.app.usage { public final class UsageStats implements android.os.Parcelable { method public int getAppLaunchCount(); + method public long getLastTimeComponentUsed(); } public final class UsageStatsManager { @@ -2095,6 +2096,7 @@ package android.bluetooth { field public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 3; // 0x3 field public static final int LE_FLAG_SIMULTANEOUS_HOST = 4; // 0x4 field public static final int LE_TK_OCTETS = 16; // 0x10 + field public static final int OOB_LENGTH_OCTETS = 2; // 0x2 field public static final int RANDOMIZER_OCTETS = 16; // 0x10 } @@ -3070,6 +3072,7 @@ package android.hardware.display { field public final float reduceBrightColorsOffset; field public final int reduceBrightColorsStrength; field public final long timeStamp; + field @NonNull public final String uniqueDisplayId; } public final class BrightnessConfiguration implements android.os.Parcelable { @@ -5194,6 +5197,21 @@ package android.media { field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce } + public final class MediaRouter2 { + method @NonNull public java.util.List<android.media.MediaRoute2Info> getAllRoutes(); + method @Nullable public String getClientPackageName(); + method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String); + method @Nullable public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String); + method public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int); + method public void startScan(); + method public void stopScan(); + method public void transfer(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRoute2Info); + } + + public abstract static class MediaRouter2.RouteCallback { + method public void onPreferredFeaturesChanged(@NonNull java.util.List<java.lang.String>); + } + public class PlayerProxy { method public void pause(); method public void setPan(float); @@ -10434,11 +10452,32 @@ package android.telecom { method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onBluetoothCallQualityReportReceived(@NonNull android.telecom.BluetoothCallQualityReport); method public abstract void onCallAudioStateChanged(@NonNull android.telecom.CallAudioState); - method @NonNull public abstract android.telecom.DiagnosticCall onInitializeDiagnosticCall(@NonNull android.telecom.Call.Details); - method public abstract void onRemoveDiagnosticCall(@NonNull android.telecom.DiagnosticCall); + method @NonNull public abstract android.telecom.CallDiagnostics onInitializeCallDiagnostics(@NonNull android.telecom.Call.Details); + method public abstract void onRemoveCallDiagnostics(@NonNull android.telecom.CallDiagnostics); field public static final String SERVICE_INTERFACE = "android.telecom.CallDiagnosticService"; } + public abstract class CallDiagnostics { + ctor public CallDiagnostics(); + method public final void clearDiagnosticMessage(int); + method public final void displayDiagnosticMessage(int, @NonNull CharSequence); + method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details); + method @Nullable public abstract CharSequence onCallDisconnected(int, int); + method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo); + method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality); + method public abstract void onReceiveDeviceToDeviceMessage(int, int); + method public final void sendDeviceToDeviceMessage(int, int); + field public static final int BATTERY_STATE_CHARGING = 3; // 0x3 + field public static final int BATTERY_STATE_GOOD = 2; // 0x2 + field public static final int BATTERY_STATE_LOW = 1; // 0x1 + field public static final int COVERAGE_GOOD = 2; // 0x2 + field public static final int COVERAGE_POOR = 1; // 0x1 + field public static final int MESSAGE_CALL_AUDIO_CODEC = 2; // 0x2 + field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1 + field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3 + field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4 + } + public static class CallScreeningService.CallResponse.Builder { method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public android.telecom.CallScreeningService.CallResponse.Builder setShouldScreenCallViaAudioProcessing(boolean); } @@ -10499,25 +10538,8 @@ package android.telecom { method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference); } - public abstract class DiagnosticCall { - ctor public DiagnosticCall(); - method public final void clearDiagnosticMessage(int); - method public final void displayDiagnosticMessage(int, @NonNull CharSequence); - method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details); - method @Nullable public abstract CharSequence onCallDisconnected(int, int); - method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo); - method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality); - method public abstract void onReceiveDeviceToDeviceMessage(int, int); - method public final void sendDeviceToDeviceMessage(int, int); - field public static final int BATTERY_STATE_CHARGING = 3; // 0x3 - field public static final int BATTERY_STATE_GOOD = 2; // 0x2 - field public static final int BATTERY_STATE_LOW = 1; // 0x1 - field public static final int COVERAGE_GOOD = 2; // 0x2 - field public static final int COVERAGE_POOR = 1; // 0x1 - field public static final int MESSAGE_CALL_AUDIO_CODEC = 2; // 0x2 - field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1 - field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3 - field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4 + @Deprecated public abstract class DiagnosticCall extends android.telecom.CallDiagnostics { + ctor @Deprecated public DiagnosticCall(); } public abstract class InCallService extends android.app.Service { @@ -10850,7 +10872,14 @@ package android.telephony { method @NonNull public static android.os.PersistableBundle getDefaultConfig(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void overrideConfig(int, @Nullable android.os.PersistableBundle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void updateConfigForPhoneId(int, String); + field public static final int GBA_DIGEST = 3; // 0x3 + field public static final int GBA_ME = 1; // 0x1 + field public static final int GBA_U = 2; // 0x2 field public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string"; + field public static final String KEY_GBA_MODE_INT = "gba_mode_int"; + field public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT = "gba_ua_security_organization_int"; + field public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = "gba_ua_security_protocol_int"; + field public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = "gba_ua_tls_cipher_suite_int"; field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool"; } @@ -11008,6 +11037,19 @@ package android.telephony { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ImsiEncryptionInfo> CREATOR; } + public final class LinkCapacityEstimate implements android.os.Parcelable { + ctor public LinkCapacityEstimate(int, int, int); + method public int describeContents(); + method public int getDownlinkCapacityKbps(); + method public int getType(); + method public int getUplinkCapacityKbps(); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LinkCapacityEstimate> CREATOR; + field public static final int INVALID = -1; // 0xffffffff + field public static final int LCE_TYPE_COMBINED = 2; // 0x2 + field public static final int LCE_TYPE_PRIMARY = 0; // 0x0 + field public static final int LCE_TYPE_SECONDARY = 1; // 0x1 + } + public final class LteVopsSupportInfo implements android.os.Parcelable { ctor public LteVopsSupportInfo(int, int); method public int describeContents(); @@ -11520,6 +11562,7 @@ package android.telephony { 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_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; // 0x24 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37; // 0x25 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 @@ -11550,6 +11593,10 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int); } + public static interface TelephonyCallback.LinkCapacityEstimateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onLinkCapacityEstimateChanged(@NonNull java.util.List<android.telephony.LinkCapacityEstimate>); + } + public static interface TelephonyCallback.OutgoingEmergencyCallListener { method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); } @@ -11621,7 +11668,6 @@ package android.telephony { method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierBandwidth getCarrierBandwidth(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 1587475bcb99..e486fa2c2e29 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -28,6 +28,7 @@ package android { 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"; + field public static final String QUERY_AUDIO_STATE = "android.permission.QUERY_AUDIO_STATE"; field public static final String QUERY_USERS = "android.permission.QUERY_USERS"; field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE"; @@ -729,6 +730,11 @@ package android.content.pm { method public static boolean isTranslucentOrFloating(android.content.res.TypedArray); field public static final long FORCE_NON_RESIZE_APP = 181136395L; // 0xacbec0bL field public static final long FORCE_RESIZE_APP = 174042936L; // 0xa5faf38L + field public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // 0xa5faf64L + field public static final long OVERRIDE_MIN_ASPECT_RATIO_LARGE = 180326787L; // 0xabf9183L + field public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 1.7777778f; + field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL + field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f; field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2 } diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index fbabfac706e1..633b986c06c9 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -25,6 +25,7 @@ import android.os.PersistableBundle; import android.os.RemoteException; import android.util.Singleton; import android.view.RemoteAnimationDefinition; +import android.window.SizeConfigurationBuckets; import com.android.internal.policy.IKeyguardDismissCallback; @@ -104,12 +105,9 @@ public class ActivityClient { } } - void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration, - int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) { + void reportSizeConfigurations(IBinder token, SizeConfigurationBuckets sizeConfigurations) { try { - getActivityClientController().reportSizeConfigurations(token, - horizontalSizeConfiguration, verticalSizeConfigurations, - smallestSizeConfigurations); + getActivityClientController().reportSizeConfigurations(token, sizeConfigurations); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8977ba774d0b..837154fd6080 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -158,7 +158,6 @@ import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseIntArray; import android.util.SuperNotCalledException; import android.util.UtilConfig; import android.util.proto.ProtoOutputStream; @@ -180,6 +179,7 @@ import android.view.contentcapture.IContentCaptureManager; import android.view.contentcapture.IContentCaptureOptionsCallback; import android.view.translation.TranslationSpec; import android.webkit.WebView; +import android.window.SizeConfigurationBuckets; import android.window.SplashScreen; import android.window.SplashScreenView; @@ -604,6 +604,8 @@ public final class ActivityThread extends ClientTransactionHandler @LifecycleState private int mLifecycleState = PRE_ON_CREATE; + private SizeConfigurationBuckets mSizeConfigurations; + @VisibleForTesting @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public ActivityClientRecord() { @@ -3764,23 +3766,8 @@ public final class ActivityThread extends ClientTransactionHandler if (configurations == null) { return; } - SparseIntArray horizontal = new SparseIntArray(); - SparseIntArray vertical = new SparseIntArray(); - SparseIntArray smallest = new SparseIntArray(); - for (int i = configurations.length - 1; i >= 0; i--) { - Configuration config = configurations[i]; - if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - vertical.put(config.screenHeightDp, 0); - } - if (config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - horizontal.put(config.screenWidthDp, 0); - } - if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { - smallest.put(config.smallestScreenWidthDp, 0); - } - } - ActivityClient.getInstance().reportSizeConfigurations(r.token, horizontal.copyKeys(), - vertical.copyKeys(), smallest.copyKeys()); + r.mSizeConfigurations = new SizeConfigurationBuckets(configurations); + ActivityClient.getInstance().reportSizeConfigurations(r.token, r.mSizeConfigurations); } private void deliverNewIntents(ActivityClientRecord r, List<ReferrerIntent> intents) { @@ -5773,7 +5760,10 @@ public final class ActivityThread extends ClientTransactionHandler // onConfigurationChanged. // TODO(b/173090263): Use diff instead after the improvement of AssetManager and // ResourcesImpl constructions. - final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig); + int diff = activity.mCurrentConfig.diffPublicOnly(newConfig); + final ActivityClientRecord cr = getActivityClient(activityToken); + diff = SizeConfigurationBuckets.filterDiff(diff, activity.mCurrentConfig, newConfig, + cr != null ? cr.mSizeConfigurations : null); if (diff == 0) { if (!shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig) diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index d310e8f0ef5c..27b19bcd31a1 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -57,7 +57,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserManager; import android.provider.DeviceConfig; -import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.LongSparseArray; @@ -90,6 +89,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -1207,9 +1207,21 @@ public class AppOpsManager { */ public static final int OP_COARSE_LOCATION_SOURCE = AppProtoEnums.APP_OP_COARSE_LOCATION_SOURCE; + /** + * Allow apps to create the requests to manage the media files without user confirmation. + * + * @see android.Manifest.permission#MANAGE_MEDIA + * @see android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection) + * @see android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean) + * @see android.provider.MediaStore#createWriteRequest(ContentResolver, Collection) + * + * @hide + */ + public static final int OP_MANAGE_MEDIA = AppProtoEnums.APP_OP_MANAGE_MEDIA; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 110; + public static final int _NUM_OP = 111; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1607,6 +1619,18 @@ public class AppOpsManager { */ public static final String OPSTR_COARSE_LOCATION_SOURCE = "android:coarse_location_source"; + /** + * Allow apps to create the requests to manage the media files without user confirmation. + * + * @see android.Manifest.permission#MANAGE_MEDIA + * @see android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection) + * @see android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean) + * @see android.provider.MediaStore#createWriteRequest(ContentResolver, Collection) + * + * @hide + */ + public static final String OPSTR_MANAGE_MEDIA = "android:manage_media"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -1688,6 +1712,7 @@ public class AppOpsManager { OP_MANAGE_ONGOING_CALLS, OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, OP_SCHEDULE_EXACT_ALARM, + OP_MANAGE_MEDIA, }; /** @@ -1809,6 +1834,7 @@ public class AppOpsManager { OP_SCHEDULE_EXACT_ALARM, // SCHEDULE_EXACT_ALARM OP_FINE_LOCATION, // OP_FINE_LOCATION_SOURCE OP_COARSE_LOCATION, // OP_COARSE_LOCATION_SOURCE + OP_MANAGE_MEDIA, // MANAGE_MEDIA }; /** @@ -1925,6 +1951,7 @@ public class AppOpsManager { OPSTR_SCHEDULE_EXACT_ALARM, OPSTR_FINE_LOCATION_SOURCE, OPSTR_COARSE_LOCATION_SOURCE, + OPSTR_MANAGE_MEDIA, }; /** @@ -2042,6 +2069,7 @@ public class AppOpsManager { "SCHEDULE_EXACT_ALARM", "FINE_LOCATION_SOURCE", "COARSE_LOCATION_SOURCE", + "MANAGE_MEDIA", }; /** @@ -2160,6 +2188,7 @@ public class AppOpsManager { Manifest.permission.SCHEDULE_EXACT_ALARM, null, // no permission for OP_ACCESS_FINE_LOCATION_SOURCE, null, // no permission for OP_ACCESS_COARSE_LOCATION_SOURCE, + Manifest.permission.MANAGE_MEDIA, }; /** @@ -2278,6 +2307,7 @@ public class AppOpsManager { null, // SCHEDULE_EXACT_ALARM null, // ACCESS_FINE_LOCATION_SOURCE null, // ACCESS_COARSE_LOCATION_SOURCE + null, // MANAGE_MEDIA }; /** @@ -2395,6 +2425,7 @@ public class AppOpsManager { null, // SCHEDULE_EXACT_ALARM null, // ACCESS_FINE_LOCATION_SOURCE null, // ACCESS_COARSE_LOCATION_SOURCE + null, // MANAGE_MEDIA }; /** @@ -2511,6 +2542,7 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM AppOpsManager.MODE_ALLOWED, // ACCESS_FINE_LOCATION_SOURCE AppOpsManager.MODE_ALLOWED, // ACCESS_COARSE_LOCATION_SOURCE + AppOpsManager.MODE_DEFAULT, // MANAGE_MEDIA }; /** @@ -2631,6 +2663,7 @@ public class AppOpsManager { false, // SCHEDULE_EXACT_ALARM false, // ACCESS_FINE_LOCATION_SOURCE false, // ACCESS_COARSE_LOCATION_SOURCE + false, // MANAGE_MEDIA }; /** @@ -8080,8 +8113,8 @@ public class AppOpsManager { } else if (collectionMode == COLLECT_SYNC // Only collect app-ops when the proxy is trusted && (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, - myUid) == PackageManager.PERMISSION_GRANTED || isTrustedVoiceServiceProxy( - mContext, mContext.getOpPackageName(), op, mContext.getUserId()))) { + myUid) == PackageManager.PERMISSION_GRANTED || + Binder.getCallingUid() == proxiedUid)) { collectNotedOpSync(op, proxiedAttributionTag); } } @@ -8092,28 +8125,6 @@ public class AppOpsManager { } } - /** - * Checks if the voice recognition service is a trust proxy. - * - * @return {@code true} if the package is a trust voice recognition service proxy - * @hide - */ - public static boolean isTrustedVoiceServiceProxy(Context context, String packageName, - int code, int userId) { - // This is a workaround for R QPR, new API change is not allowed. We only allow the current - // voice recognizer is also the voice interactor to noteproxy op. - if (code != OP_RECORD_AUDIO) { - return false; - } - final String voiceRecognitionComponent = Settings.Secure.getStringForUser( - context.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE, userId); - - final String voiceRecognitionServicePackageName = - getComponentPackageNameFromString(voiceRecognitionComponent); - return (Objects.equals(packageName, voiceRecognitionServicePackageName)) - && isPackagePreInstalled(context, packageName, userId); - } - private static String getComponentPackageNameFromString(String from) { ComponentName componentName = from != null ? ComponentName.unflattenFromString(from) : null; return componentName != null ? componentName.getPackageName() : ""; @@ -8488,8 +8499,7 @@ public class AppOpsManager { // Only collect app-ops when the proxy is trusted && (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, Process.myUid()) == PackageManager.PERMISSION_GRANTED - || isTrustedVoiceServiceProxy(mContext, mContext.getOpPackageName(), opInt, - mContext.getUserId()))) { + || Binder.getCallingUid() == proxiedUid)) { collectNotedOpSync(opInt, proxiedAttributionTag); } } @@ -9138,7 +9148,7 @@ public class AppOpsManager { try { sFullLog = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, FULL_LOG, false); - } catch (SecurityException e) { + } catch (Exception e) { // This should not happen, but it may, in rare cases sFullLog = false; } diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index 573931ed228e..ed4836e31209 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -25,6 +25,7 @@ import android.content.res.Configuration; import android.os.Bundle; import android.os.PersistableBundle; import android.view.RemoteAnimationDefinition; +import android.window.SizeConfigurationBuckets; import com.android.internal.policy.IKeyguardDismissCallback; @@ -49,8 +50,8 @@ interface IActivityClientController { oneway void activityDestroyed(in IBinder token); oneway void activityRelaunched(in IBinder token); - oneway void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration, - in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations); + oneway void reportSizeConfigurations(in IBinder token, + in SizeConfigurationBuckets sizeConfigurations); 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, diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 5402381b7207..e83557c18f8f 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -167,7 +167,8 @@ interface IWallpaperManager { * @hide */ void removeOnLocalColorsChangedListener( - in ILocalWallpaperColorConsumer callback, int which, int userId, int displayId); + in ILocalWallpaperColorConsumer callback, in List<RectF> area, + int which, int userId, int displayId); /** * @hide diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index afcf63b0f1e2..1765849c8314 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -331,10 +331,12 @@ public class StatusBarManager { * @hide */ @RequiresPermission(android.Manifest.permission.STATUS_BAR) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Send {@link " - + "android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS} instead.") + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "This operation" + + " is not allowed anymore, please see {@link android.content" + + ".Intent#ACTION_CLOSE_SYSTEM_DIALOGS} for more details.") @TestApi public void collapsePanels() { + try { final IStatusBarService svc = getService(); if (svc != null) { diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 7dbbc54665e9..3ef6757ade60 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -68,6 +68,7 @@ import android.os.StrictMode; import android.os.SystemProperties; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Pair; import android.view.Display; @@ -318,8 +319,20 @@ public class WallpaperManager { private int mCachedWallpaperUserId; private Bitmap mDefaultWallpaper; private Handler mMainLooperHandler; - private ArrayMap<LocalWallpaperColorConsumer, ILocalWallpaperColorConsumer> - mLocalColorCallbacks = new ArrayMap<>(); + private ArrayMap<RectF, ArraySet<LocalWallpaperColorConsumer>> mLocalColorAreas = + new ArrayMap<>(); + private ILocalWallpaperColorConsumer mLocalColorCallback = + new ILocalWallpaperColorConsumer.Stub() { + @Override + public void onColorsChanged(RectF area, WallpaperColors colors) { + ArraySet<LocalWallpaperColorConsumer> callbacks = + mLocalColorAreas.get(area); + if (callbacks == null) return; + for (LocalWallpaperColorConsumer callback: callbacks) { + callback.onColorsChanged(area, colors); + } + } + }; Globals(IWallpaperManager service, Looper looper) { mService = service; @@ -361,37 +374,46 @@ public class WallpaperManager { } } - private ILocalWallpaperColorConsumer wrap(LocalWallpaperColorConsumer callback) { - ILocalWallpaperColorConsumer callback2 = new ILocalWallpaperColorConsumer.Stub() { - @Override - public void onColorsChanged(RectF area, WallpaperColors colors) { - callback.onColorsChanged(area, colors); - } - }; - mLocalColorCallbacks.put(callback, callback2); - return callback2; - } - public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback, @NonNull List<RectF> regions, int which, int userId, int displayId) { + for (RectF area: regions) { + ArraySet<LocalWallpaperColorConsumer> callbacks = mLocalColorAreas.get(area); + if (callbacks == null) { + callbacks = new ArraySet<>(); + mLocalColorAreas.put(area, callbacks); + } + callbacks.add(callback); + } try { - mService.addOnLocalColorsChangedListener(wrap(callback) , regions, which, + mService.addOnLocalColorsChangedListener(mLocalColorCallback , regions, which, userId, displayId); } catch (RemoteException e) { // Can't get colors, connection lost. + Log.e(TAG, "Can't register for local color updates", e); } } public void removeOnColorsChangedListener( @NonNull LocalWallpaperColorConsumer callback, int which, int userId, int displayId) { - ILocalWallpaperColorConsumer callback2 = mLocalColorCallbacks.remove(callback); - if (callback2 == null) return; + final ArrayList<RectF> removeAreas = new ArrayList<>(); + for (RectF area : mLocalColorAreas.keySet()) { + ArraySet<LocalWallpaperColorConsumer> callbacks = mLocalColorAreas.get(area); + if (callbacks == null) continue; + callbacks.remove(callback); + if (callbacks.size() == 0) { + mLocalColorAreas.remove(area); + removeAreas.add(area); + } + } try { - mService.removeOnLocalColorsChangedListener( - callback2, which, userId, displayId); + if (removeAreas.size() > 0) { + mService.removeOnLocalColorsChangedListener( + mLocalColorCallback, removeAreas, which, userId, displayId); + } } catch (RemoteException e) { // Can't get colors, connection lost. + Log.e(TAG, "Can't unregister for local color updates", e); } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 930717b97555..56aa9a2b580c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2987,7 +2987,7 @@ public class DevicePolicyManager { /** * Checks if it's safe to run operations that can be affected by the given {@code reason}. * - * <p><b>Note:/b> notice that the operation safety state might change between the time this + * <p><b>Note:</b> notice that the operation safety state might change between the time this * method returns and the operation's method is called, so calls to the latter could still throw * a {@link UnsafeStateException} even when this method returns {@code true}. * diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index 94ab0dd00113..9f8fcc15b747 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -253,7 +253,7 @@ public class LaunchActivityItem extends ClientTransactionItem { return other == null; } return other != null && mInfo.flags == other.flags - && mInfo.maxAspectRatio == other.maxAspectRatio + && mInfo.getMaxAspectRatio() == other.getMaxAspectRatio() && Objects.equals(mInfo.launchToken, other.launchToken) && Objects.equals(mInfo.getComponentName(), other.getComponentName()); } diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index dcecd9070a74..5d50c5d77887 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -20,6 +20,7 @@ import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED; import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; +import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED; import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.END_OF_DAY; @@ -109,6 +110,13 @@ public final class UsageStats implements Parcelable { public long mTotalTimeForegroundServiceUsed; /** + * Last time this package's component is used, measured in milliseconds since the epoch. + * See {@link UsageEvents.Event#APP_COMPONENT_USED} + * @hide + */ + public long mLastTimeComponentUsed; + + /** * {@hide} */ @UnsupportedAppUsage @@ -166,6 +174,7 @@ public final class UsageStats implements Parcelable { mEndTimeStamp = stats.mEndTimeStamp; mLastTimeUsed = stats.mLastTimeUsed; mLastTimeVisible = stats.mLastTimeVisible; + mLastTimeComponentUsed = stats.mLastTimeComponentUsed; mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed; mTotalTimeInForeground = stats.mTotalTimeInForeground; mTotalTimeVisible = stats.mTotalTimeVisible; @@ -265,6 +274,16 @@ public final class UsageStats implements Parcelable { } /** + * Get the last time this package's component was used, measured in milliseconds since the + * epoch. + * @hide + */ + @SystemApi + public long getLastTimeComponentUsed() { + return mLastTimeComponentUsed; + } + + /** * Returns the number of times the app was launched as an activity from outside of the app. * Excludes intra-app activity transitions. * @hide @@ -323,6 +342,7 @@ public final class UsageStats implements Parcelable { mergeEventMap(mForegroundServices, right.mForegroundServices); mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed); mLastTimeVisible = Math.max(mLastTimeVisible, right.mLastTimeVisible); + mLastTimeComponentUsed = Math.max(mLastTimeComponentUsed, right.mLastTimeComponentUsed); mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed, right.mLastTimeForegroundServiceUsed); } @@ -598,6 +618,9 @@ public final class UsageStats implements Parcelable { mLastTimeVisible = timeStamp; } break; + case APP_COMPONENT_USED: + mLastTimeComponentUsed = timeStamp; + break; default: break; } @@ -620,6 +643,7 @@ public final class UsageStats implements Parcelable { dest.writeLong(mEndTimeStamp); dest.writeLong(mLastTimeUsed); dest.writeLong(mLastTimeVisible); + dest.writeLong(mLastTimeComponentUsed); dest.writeLong(mLastTimeForegroundServiceUsed); dest.writeLong(mTotalTimeInForeground); dest.writeLong(mTotalTimeVisible); @@ -674,6 +698,7 @@ public final class UsageStats implements Parcelable { stats.mEndTimeStamp = in.readLong(); stats.mLastTimeUsed = in.readLong(); stats.mLastTimeVisible = in.readLong(); + stats.mLastTimeComponentUsed = in.readLong(); stats.mLastTimeForegroundServiceUsed = in.readLong(); stats.mTotalTimeInForeground = in.readLong(); stats.mTotalTimeVisible = in.readLong(); diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 8fd0de7dbb39..a6b4b47f0db2 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -520,9 +520,7 @@ public class AppWidgetHostView extends FrameLayout { return; } int layoutId = rvToApply.getLayoutId(); - // If our stale view has been prepared to match active, and the new - // layout matches, try recycling it - if (content == null && layoutId == mLayoutId) { + if (rvToApply.canRecycleView(mView)) { try { rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize, mColorResources); diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java index 98107461fb4c..08d694eb93e2 100644 --- a/core/java/android/bluetooth/OobData.java +++ b/core/java/android/bluetooth/OobData.java @@ -76,7 +76,7 @@ public final class OobData implements Parcelable { private static final String TAG = "OobData"; /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */ @SystemApi - private static final int OOB_LENGTH_OCTETS = 2; + public static final int OOB_LENGTH_OCTETS = 2; /** * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1). * (AD 3.1.2) (CSS 1.6.2) @@ -590,7 +590,6 @@ public final class OobData implements Parcelable { * * @hide */ - @SystemApi private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) { Preconditions.checkNotNull(confirmationHash); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index f8dd0e1452e5..190ef1adeb85 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3876,7 +3876,7 @@ public abstract class Context { * @see #getSystemService(String) * @hide */ - public static final String POWER_STATS_SERVICE = "power_stats"; + public static final String POWER_STATS_SERVICE = "powerstats"; /** * Use with {@link #getSystemService(String)} to retrieve a diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 5cf83acd9a75..adf9ff32c4ec 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1954,8 +1954,8 @@ public class Intent implements Parcelable, Cloneable { /** * Activity action: Launch UI to show information about the usage - * of a given permission. This action would be handled by apps that - * want to show details about how and why given permission is being + * of a given permission group. This action would be handled by apps that + * want to show details about how and why given permission group is being * used. * <p> * <strong>Important:</strong>You must protect the activity that handles @@ -1965,7 +1965,7 @@ public class Intent implements Parcelable, Cloneable { * activities that are not properly protected. * * <p> - * Input: {@code android.intent.extra.PERMISSION_NAME} specifies the permission + * Input: {@link android.Manifest.permission_group} specifies the permission group * for which the launched UI would be targeted. * </p> * <p> diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 58f83a73ff16..feb58a30e519 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -21,6 +21,7 @@ import android.annotation.TestApi; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; +import android.compat.annotation.Overridable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Intent; @@ -254,7 +255,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @See {@link android.R.attr#maxAspectRatio}. * @hide */ - public float maxAspectRatio; + private float mMaxAspectRatio; /** * Value indicating the minimum aspect ratio the activity supports. @@ -263,7 +264,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @See {@link android.R.attr#minAspectRatio}. * @hide */ - public float minAspectRatio; + private float mMinAspectRatio; /** * Indicates that the activity works well with size changes like display changing size. @@ -948,6 +949,57 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public @interface SizeChangesSupportMode {} /** + * This change id is the gatekeeper for all treatments that force a given min aspect ratio. + * Enabling this change will allow the following min aspect ratio treatments to be applied: + * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM + * OVERRIDE_MIN_ASPECT_RATIO_LARGE + * + * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's + * manifest will be overridden to the largest enabled aspect ratio treatment unless the app's + * manifest value is higher. + * @hide + */ + @ChangeId + @Overridable + @Disabled + @TestApi + public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // buganizer id + + /** + * This change id sets the activity's min aspect ratio to a medium value as defined by + * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE. + * + * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled. + * @hide + */ + @ChangeId + @Overridable + @Disabled + @TestApi + public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // buganizer id + + /** @hide Medium override aspect ratio, currently 3:2. */ + @TestApi + public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 3 / 2f; + + /** + * This change id sets the activity's min aspect ratio to a large value as defined by + * OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE. + * + * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled. + * @hide + */ + @ChangeId + @Overridable + @Disabled + @TestApi + public static final long OVERRIDE_MIN_ASPECT_RATIO_LARGE = 180326787L; // buganizer id + + /** @hide Large override aspect ratio, currently 16:9 */ + @TestApi + public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 16 / 9f; + + /** * Convert Java change bits to native. * * @hide @@ -1118,8 +1170,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { requestedVrComponent = orig.requestedVrComponent; rotationAnimation = orig.rotationAnimation; colorMode = orig.colorMode; - maxAspectRatio = orig.maxAspectRatio; - minAspectRatio = orig.minAspectRatio; + mMaxAspectRatio = orig.mMaxAspectRatio; + mMinAspectRatio = orig.mMinAspectRatio; supportsSizeChanges = orig.supportsSizeChanges; attributionTags = orig.attributionTags; } @@ -1149,7 +1201,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @hide */ public boolean hasFixedAspectRatio() { - return maxAspectRatio != 0 || minAspectRatio != 0; + return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0; } /** @@ -1262,6 +1314,58 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { } /** @hide */ + public void setMaxAspectRatio(float maxAspectRatio) { + this.mMaxAspectRatio = maxAspectRatio; + } + + /** @hide */ + public float getMaxAspectRatio() { + return mMaxAspectRatio; + } + + /** @hide */ + public void setMinAspectRatio(float minAspectRatio) { + this.mMinAspectRatio = minAspectRatio; + } + + /** + * Returns the min aspect ratio of this activity. + * + * This takes into account the minimum aspect ratio as defined in the app's manifest and + * possible overrides as per OVERRIDE_MIN_ASPECT_RATIO. + * + * In the rare cases where the manifest minimum aspect ratio is required, use + * {@code getManifestMinAspectRatio}. + * @hide + */ + public float getMinAspectRatio() { + if (applicationInfo == null || !CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO, + applicationInfo.packageName, + UserHandle.getUserHandleForUid(applicationInfo.uid))) { + return mMinAspectRatio; + } + + if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE, + applicationInfo.packageName, + UserHandle.getUserHandleForUid(applicationInfo.uid))) { + return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio); + } + + if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM, + applicationInfo.packageName, + UserHandle.getUserHandleForUid(applicationInfo.uid))) { + return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio); + } + + return mMinAspectRatio; + } + + /** @hide */ + public float getManifestMinAspectRatio() { + return mMinAspectRatio; + } + + /** @hide */ @UnsupportedAppUsage public static boolean isResizeableMode(int mode) { return mode == RESIZE_MODE_RESIZEABLE @@ -1360,11 +1464,14 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { if (requestedVrComponent != null) { pw.println(prefix + "requestedVrComponent=" + requestedVrComponent); } - if (maxAspectRatio != 0) { - pw.println(prefix + "maxAspectRatio=" + maxAspectRatio); + if (getMaxAspectRatio() != 0) { + pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio()); } - if (minAspectRatio != 0) { - pw.println(prefix + "minAspectRatio=" + minAspectRatio); + if (getMinAspectRatio() != 0) { + pw.println(prefix + "minAspectRatio=" + getMinAspectRatio()); + if (getManifestMinAspectRatio() != getMinAspectRatio()) { + pw.println(prefix + "getManifestMinAspectRatio=" + getManifestMinAspectRatio()); + } } if (supportsSizeChanges) { pw.println(prefix + "supportsSizeChanges=true"); @@ -1420,8 +1527,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { dest.writeString8(requestedVrComponent); dest.writeInt(rotationAnimation); dest.writeInt(colorMode); - dest.writeFloat(maxAspectRatio); - dest.writeFloat(minAspectRatio); + dest.writeFloat(mMaxAspectRatio); + dest.writeFloat(mMinAspectRatio); dest.writeBoolean(supportsSizeChanges); dest.writeString8Array(attributionTags); } @@ -1540,8 +1647,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { requestedVrComponent = source.readString8(); rotationAnimation = source.readInt(); colorMode = source.readInt(); - maxAspectRatio = source.readFloat(); - minAspectRatio = source.readFloat(); + mMaxAspectRatio = source.readFloat(); + mMinAspectRatio = source.readFloat(); supportsSizeChanges = source.readBoolean(); attributionTags = source.createString8Array(); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 5e08399f8abb..5ff11240db72 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -4890,8 +4890,8 @@ public class PackageParser { info.maxRecents = target.info.maxRecents; info.windowLayout = target.info.windowLayout; info.resizeMode = target.info.resizeMode; - info.maxAspectRatio = target.info.maxAspectRatio; - info.minAspectRatio = target.info.minAspectRatio; + info.setMaxAspectRatio(target.info.getMaxAspectRatio()); + info.setMinAspectRatio(target.info.getManifestMinAspectRatio()); info.supportsSizeChanges = target.info.supportsSizeChanges; info.requestedVrComponent = target.info.requestedVrComponent; @@ -8157,7 +8157,7 @@ public class PackageParser { return; } - info.maxAspectRatio = maxAspectRatio; + info.setMaxAspectRatio(maxAspectRatio); mHasMaxAspectRatio = true; } @@ -8173,7 +8173,7 @@ public class PackageParser { return; } - info.minAspectRatio = minAspectRatio; + info.setMinAspectRatio(minAspectRatio); mHasMinAspectRatio = true; } diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index b660a00443a4..fdd2c2ab83e3 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -474,9 +474,9 @@ public class PackageInfoWithoutStateUtils { ai.screenOrientation = a.getScreenOrientation(); ai.resizeMode = a.getResizeMode(); Float maxAspectRatio = a.getMaxAspectRatio(); - ai.maxAspectRatio = maxAspectRatio != null ? maxAspectRatio : 0f; + ai.setMaxAspectRatio(maxAspectRatio != null ? maxAspectRatio : 0f); Float minAspectRatio = a.getMinAspectRatio(); - ai.minAspectRatio = minAspectRatio != null ? minAspectRatio : 0f; + ai.setMinAspectRatio(minAspectRatio != null ? minAspectRatio : 0f); ai.supportsSizeChanges = a.getSupportsSizeChanges(); ai.requestedVrComponent = a.getRequestedVrComponent(); ai.rotationAnimation = a.getRotationAnimation(); diff --git a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java index 0b81c6c8cc25..909f456dd433 100644 --- a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java +++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java @@ -31,23 +31,31 @@ public class SensorPropertiesInternal implements Parcelable { public final int sensorId; @SensorProperties.Strength public final int sensorStrength; public final int maxEnrollmentsPerUser; + public final boolean resetLockoutRequiresHardwareAuthToken; + public final boolean resetLockoutRequiresChallenge; public static SensorPropertiesInternal from(@NonNull SensorPropertiesInternal prop) { return new SensorPropertiesInternal(prop.sensorId, prop.sensorStrength, - prop.maxEnrollmentsPerUser); + prop.maxEnrollmentsPerUser, prop.resetLockoutRequiresHardwareAuthToken, + prop.resetLockoutRequiresChallenge); } protected SensorPropertiesInternal(int sensorId, @SensorProperties.Strength int sensorStrength, - int maxEnrollmentsPerUser) { + int maxEnrollmentsPerUser, boolean resetLockoutRequiresHardwareAuthToken, + boolean resetLockoutRequiresChallenge) { this.sensorId = sensorId; this.sensorStrength = sensorStrength; this.maxEnrollmentsPerUser = maxEnrollmentsPerUser; + this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken; + this.resetLockoutRequiresChallenge = resetLockoutRequiresChallenge; } protected SensorPropertiesInternal(Parcel in) { sensorId = in.readInt(); sensorStrength = in.readInt(); maxEnrollmentsPerUser = in.readInt(); + resetLockoutRequiresHardwareAuthToken = in.readBoolean(); + resetLockoutRequiresChallenge = in.readBoolean(); } public static final Creator<SensorPropertiesInternal> CREATOR = @@ -73,6 +81,8 @@ public class SensorPropertiesInternal implements Parcelable { dest.writeInt(sensorId); dest.writeInt(sensorStrength); dest.writeInt(maxEnrollmentsPerUser); + dest.writeBoolean(resetLockoutRequiresHardwareAuthToken); + dest.writeBoolean(resetLockoutRequiresChallenge); } @Override diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java index a6c6b46d5b81..6b7d8c3dde49 100644 --- a/core/java/android/hardware/display/BrightnessChangeEvent.java +++ b/core/java/android/hardware/display/BrightnessChangeEvent.java @@ -47,6 +47,10 @@ public final class BrightnessChangeEvent implements Parcelable { * @hide */ public final int userId; + /** The unique id of the screen on which the brightness was changed */ + @NonNull + public final String uniqueDisplayId; + /** Lux values of recent sensor data */ public final float[] luxValues; @@ -120,15 +124,16 @@ public final class BrightnessChangeEvent implements Parcelable { /** @hide */ private BrightnessChangeEvent(float brightness, long timeStamp, String packageName, - int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel, - float powerBrightnessFactor, boolean nightMode, int colorTemperature, - boolean reduceBrightColors, int reduceBrightColorsStrength, + int userId, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps, + float batteryLevel, float powerBrightnessFactor, boolean nightMode, + int colorTemperature, boolean reduceBrightColors, int reduceBrightColorsStrength, float reduceBrightColorsOffset, float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness, long[] colorValueBuckets, long colorSampleDuration) { this.brightness = brightness; this.timeStamp = timeStamp; this.packageName = packageName; this.userId = userId; + this.uniqueDisplayId = uniqueDisplayId; this.luxValues = luxValues; this.luxTimestamps = luxTimestamps; this.batteryLevel = batteryLevel; @@ -151,6 +156,7 @@ public final class BrightnessChangeEvent implements Parcelable { this.timeStamp = other.timeStamp; this.packageName = redactPackage ? null : other.packageName; this.userId = other.userId; + this.uniqueDisplayId = other.uniqueDisplayId; this.luxValues = other.luxValues; this.luxTimestamps = other.luxTimestamps; this.batteryLevel = other.batteryLevel; @@ -172,6 +178,7 @@ public final class BrightnessChangeEvent implements Parcelable { timeStamp = source.readLong(); packageName = source.readString(); userId = source.readInt(); + uniqueDisplayId = source.readString(); luxValues = source.createFloatArray(); luxTimestamps = source.createLongArray(); batteryLevel = source.readFloat(); @@ -209,6 +216,7 @@ public final class BrightnessChangeEvent implements Parcelable { dest.writeLong(timeStamp); dest.writeString(packageName); dest.writeInt(userId); + dest.writeString(uniqueDisplayId); dest.writeFloatArray(luxValues); dest.writeLongArray(luxTimestamps); dest.writeFloat(batteryLevel); @@ -231,6 +239,7 @@ public final class BrightnessChangeEvent implements Parcelable { private long mTimeStamp; private String mPackageName; private int mUserId; + private String mUniqueDisplayId; private float[] mLuxValues; private long[] mLuxTimestamps; private float mBatteryLevel; @@ -270,6 +279,12 @@ public final class BrightnessChangeEvent implements Parcelable { return this; } + /** {@see BrightnessChangeEvent#uniqueScreenId} */ + public Builder setUniqueDisplayId(String uniqueId) { + mUniqueDisplayId = uniqueId; + return this; + } + /** {@see BrightnessChangeEvent#luxValues} */ public Builder setLuxValues(float[] luxValues) { mLuxValues = luxValues; @@ -354,11 +369,11 @@ public final class BrightnessChangeEvent implements Parcelable { /** Builds a BrightnessChangeEvent */ public BrightnessChangeEvent build() { return new BrightnessChangeEvent(mBrightness, mTimeStamp, - mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel, - mPowerBrightnessFactor, mNightMode, mColorTemperature, mReduceBrightColors, - mReduceBrightColorsStrength, mReduceBrightColorsOffset, mLastBrightness, - mIsDefaultBrightnessConfig, mIsUserSetBrightness, mColorValueBuckets, - mColorSampleDuration); + mPackageName, mUserId, mUniqueDisplayId, mLuxValues, mLuxTimestamps, + mBatteryLevel, mPowerBrightnessFactor, mNightMode, mColorTemperature, + mReduceBrightColors, mReduceBrightColorsStrength, mReduceBrightColorsOffset, + mLastBrightness, mIsDefaultBrightnessConfig, mIsUserSetBrightness, + mColorValueBuckets, mColorSampleDuration); } } } diff --git a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java index b9c0d12de22b..34cbcb417e1b 100644 --- a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java +++ b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java @@ -41,8 +41,11 @@ public class FaceSensorPropertiesInternal extends SensorPropertiesInternal { */ public FaceSensorPropertiesInternal(int sensorId, @SensorProperties.Strength int strength, int maxEnrollmentsPerUser, boolean supportsFaceDetection, - boolean supportsSelfIllumination) { - super(sensorId, strength, maxEnrollmentsPerUser); + boolean supportsSelfIllumination, boolean resetLockoutRequiresChallenge) { + // resetLockout is managed by the HAL and requires a HardwareAuthToken for all face + // HAL interfaces (IBiometricsFace@1.0 HIDL and IFace@1.0 AIDL). + super(sensorId, strength, maxEnrollmentsPerUser, + true /* resetLockoutRequiresHardwareAuthToken */, resetLockoutRequiresChallenge); this.supportsFaceDetection = supportsFaceDetection; this.supportsSelfIllumination = supportsSelfIllumination; } diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java index 51addc95ac79..adc61a744f89 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java @@ -36,12 +36,6 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna public final @FingerprintSensorProperties.SensorType int sensorType; /** - * IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT - * cannot be checked - */ - public final boolean resetLockoutRequiresHardwareAuthToken; - - /** * The location of the center of the sensor if applicable. For example, sensors of type * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the * distance in pixels, measured from the left edge of the screen. @@ -68,9 +62,13 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna @FingerprintSensorProperties.SensorType int sensorType, boolean resetLockoutRequiresHardwareAuthToken, int sensorLocationX, int sensorLocationY, int sensorRadius) { - super(sensorId, strength, maxEnrollmentsPerUser); + // IBiometricsFingerprint@2.1 handles lockout in the framework, so the challenge is not + // required as it can only be generated/attested/verified by TEE components. + // IFingerprint@1.0 handles lockout below the HAL, but does not require a challenge. See + // the HAL interface for more details. + super(sensorId, strength, maxEnrollmentsPerUser, resetLockoutRequiresHardwareAuthToken, + false /* resetLockoutRequiresChallenge */); this.sensorType = sensorType; - this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken; this.sensorLocationX = sensorLocationX; this.sensorLocationY = sensorLocationY; this.sensorRadius = sensorRadius; @@ -98,9 +96,9 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna @SensorProperties.Strength int strength, int maxEnrollmentsPerUser, @FingerprintSensorProperties.SensorType int sensorType, boolean resetLockoutRequiresHardwareAuthToken) { - super(sensorId, strength, maxEnrollmentsPerUser); + super(sensorId, strength, maxEnrollmentsPerUser, resetLockoutRequiresHardwareAuthToken, + false /* resetLockoutRequiresChallenge */); this.sensorType = sensorType; - this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken; int[] props = context.getResources().getIntArray( com.android.internal.R.array.config_udfps_sensor_props); @@ -119,7 +117,6 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna protected FingerprintSensorPropertiesInternal(Parcel in) { super(in); sensorType = in.readInt(); - resetLockoutRequiresHardwareAuthToken = in.readBoolean(); sensorLocationX = in.readInt(); sensorLocationY = in.readInt(); sensorRadius = in.readInt(); @@ -147,7 +144,6 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(sensorType); - dest.writeBoolean(resetLockoutRequiresHardwareAuthToken); dest.writeInt(sensorLocationX); dest.writeInt(sensorLocationY); dest.writeInt(sensorRadius); diff --git a/core/res/res/values/vendor_allowed_personal_apps_org_owned_device.xml b/core/java/android/net/NetworkScore.aidl index 0435c301a2da..af12dcf7f17a 100644 --- a/core/res/res/values/vendor_allowed_personal_apps_org_owned_device.xml +++ b/core/java/android/net/NetworkScore.aidl @@ -1,7 +1,5 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- /** - * Copyright (C) 2020 The Android Open Source Project + * Copyright (c) 2021, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ ---> -<resources> - <!-- A list of apps to be allowed in the personal profile of an organization-owned device. --> - <string-array translatable="false" name="vendor_allowed_personal_apps_org_owned_device"> - </string-array> -</resources> + +package android.net; + +parcelable NetworkScore; + diff --git a/core/java/android/net/NetworkScore.java b/core/java/android/net/NetworkScore.java new file mode 100644 index 000000000000..f47801002296 --- /dev/null +++ b/core/java/android/net/NetworkScore.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.net; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Object representing the quality of a network as perceived by the user. + * + * A NetworkScore object represents the characteristics of a network that affects how good the + * network is considered for a particular use. + * @hide + */ +// TODO : @SystemApi when the implementation is complete +public final class NetworkScore implements Parcelable { + // This will be removed soon. Do *NOT* depend on it for any new code that is not part of + // a migration. + private final int mLegacyInt; + + /** @hide */ + NetworkScore(final int legacyInt) { + this.mLegacyInt = legacyInt; + } + + private NetworkScore(@NonNull final Parcel in) { + mLegacyInt = in.readInt(); + } + + public int getLegacyInt() { + return mLegacyInt; + } + + @Override + public String toString() { + return "Score(" + mLegacyInt + ")"; + } + + @Override + public void writeToParcel(@NonNull final Parcel dest, final int flags) { + dest.writeInt(mLegacyInt); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull public static final Creator<NetworkScore> CREATOR = new Creator<>() { + @Override + @NonNull + public NetworkScore createFromParcel(@NonNull final Parcel in) { + return new NetworkScore(in); + } + + @Override + @NonNull + public NetworkScore[] newArray(int size) { + return new NetworkScore[size]; + } + }; + + /** + * A builder for NetworkScore. + */ + public static final class Builder { + private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE; + private int mLegacyInt = INVALID_LEGACY_INT; + + /** + * Sets the legacy int for this score. + * + * Do not rely on this. It will be gone by the time S is released. + * + * @param score the legacy int + * @return this + */ + @NonNull + public Builder setLegacyInt(final int score) { + mLegacyInt = score; + return this; + } + + /** + * Builds this NetworkScore. + * @return The built NetworkScore object. + */ + @NonNull + public NetworkScore build() { + return new NetworkScore(mLegacyInt); + } + } +} diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 4c26e2f33fb2..fa6472ee4a79 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -986,13 +986,13 @@ public abstract class BatteryStats implements Parcelable { public abstract void getDeferredJobsLineLocked(StringBuilder sb, int which); /** - * Returns the battery consumption (in microcoulombs) of the screen while on and uid active, + * Returns the battery consumption (in microcoulombs) of bluetooth for this uid, * derived from on device power measurement data. * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. * * {@hide} */ - public abstract long getScreenOnMeasuredBatteryConsumptionUC(); + public abstract long getBluetoothMeasuredBatteryConsumptionUC(); /** * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from @@ -1004,6 +1004,24 @@ public abstract class BatteryStats implements Parcelable { public abstract long getCpuMeasuredBatteryConsumptionUC(); /** + * Returns the battery consumption (in microcoulombs) of the screen while on and uid active, + * derived from on device power measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getScreenOnMeasuredBatteryConsumptionUC(); + + /** + * Returns the battery consumption (in microcoulombs) of wifi for this uid, + * derived from on device power measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getWifiMeasuredBatteryConsumptionUC(); + + /** * Returns the battery consumption (in microcoulombs) used by this uid for each * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}). @@ -2505,11 +2523,29 @@ public abstract class BatteryStats implements Parcelable { }; /** - * Returned value if power data is unavailable + * Returned value if power data is unavailable. + * + * {@hide} + */ + public static final long POWER_DATA_UNAVAILABLE = -1L; + + /** + * Returns the battery consumption (in microcoulombs) of bluetooth, derived from on + * device power measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. * * {@hide} */ - public static final long POWER_DATA_UNAVAILABLE = -1; + public abstract long getBluetoothMeasuredBatteryConsumptionUC(); + + /** + * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power + * measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getCpuMeasuredBatteryConsumptionUC(); /** * Returns the battery consumption (in microcoulombs) of the screen while on, derived from on @@ -2530,13 +2566,13 @@ public abstract class BatteryStats implements Parcelable { public abstract long getScreenDozeMeasuredBatteryConsumptionUC(); /** - * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power - * measurement data. + * Returns the battery consumption (in microcoulombs) of wifi, derived from on + * device power measurement data. * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. * * {@hide} */ - public abstract long getCpuMeasuredBatteryConsumptionUC(); + public abstract long getWifiMeasuredBatteryConsumptionUC(); /** * Returns the battery consumption (in microcoulombs) that each diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 0d9f715c11d4..a2edc93c6e5e 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -140,7 +140,7 @@ public class Build { */ @UnsupportedAppUsage @TestApi - public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1"); + public static final boolean IS_EMULATOR = getString("ro.boot.qemu").equals("1"); /** * A hardware serial number, if available. Alphanumeric only, case-insensitive. diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index a19728c5c498..2c4d130788cc 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -197,6 +197,9 @@ public final class PowerManager { * application as the normal behavior. Notifications that pop up and want * the device to be on are the exception; use this flag to be like them. * </p><p> + * Android TV playback devices attempt to turn on the HDMI-connected TV via HDMI-CEC on any + * wake-up, including wake-ups triggered by wake locks. + * </p><p> * Cannot be used with {@link #PARTIAL_WAKE_LOCK}. * </p> */ @@ -1299,6 +1302,9 @@ public final class PowerManager { * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT * default display group} is already off then nothing will happen. * + * <p>If the device is an Android TV playback device and the current active source on the + * HDMI-connected TV, it will attempt to turn off that TV via HDMI-CEC. + * * <p> * Overrides all the wake locks that are held. * This is what happens when the power key is pressed to turn off the screen. @@ -1430,6 +1436,10 @@ public final class PowerManager { * be turned on. Additionally, if the device is asleep it will be awoken. If the {@link * android.view.Display#DEFAULT_DISPLAY default display} is already on then nothing will happen. * + * <p>If the device is an Android TV playback device, it will attempt to turn on the + * HDMI-connected TV and become the current active source via the HDMI-CEC One Touch Play + * feature. + * * <p> * This is what happens when the power key is pressed to turn on the screen. * </p><p> diff --git a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java index 016cc2f3cdad..ad74a9f5c906 100644 --- a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java +++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java @@ -110,7 +110,7 @@ public final class WifiActivityEnergyInfo implements Parcelable { } // Calculate energy used using PowerProfile. PowerProfile powerProfile = new PowerProfile(context); - final double rxIdleCurrent = powerProfile.getAveragePower( + final double idleCurrent = powerProfile.getAveragePower( PowerProfile.POWER_WIFI_CONTROLLER_IDLE); final double rxCurrent = powerProfile.getAveragePower( PowerProfile.POWER_WIFI_CONTROLLER_RX); @@ -121,7 +121,7 @@ public final class WifiActivityEnergyInfo implements Parcelable { return (long) ((txDurationMillis * txCurrent + rxDurationMillis * rxCurrent - + idleDurationMillis * rxIdleCurrent) + + idleDurationMillis * idleCurrent) * voltage); } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index bae36b299247..177e422e7851 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -41,6 +41,7 @@ import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.permission.SplitPermissionInfoParcelable; +import android.location.LocationManager; import android.media.AudioManager; import android.os.Build; import android.os.Handler; @@ -50,12 +51,14 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.provider.DeviceConfig; import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; import android.util.Log; import android.util.Slog; +import com.android.internal.R; import com.android.internal.annotations.Immutable; import com.android.internal.util.CollectionUtils; @@ -82,6 +85,8 @@ public final class PermissionManager { public static final String KILL_APP_REASON_GIDS_CHANGED = "permission grant or revoke changed gids"; + private static final String SYSTEM_PKG = "android"; + /** * Refuse to install package if groups of permissions are bad * - Permission groups should only be shared between apps sharing a certificate @@ -857,6 +862,23 @@ public final class PermissionManager { } /** + * Check if this package/op combination is exempted from indicators + * @return + * @hide + */ + public static boolean isSpecialCaseShownIndicator(@NonNull Context context, + @NonNull String packageName) { + + if (packageName.equals(SYSTEM_PKG)) { + return false; + } + + return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled", + false) + || packageName.equals(context.getString(R.string.config_systemSpeechRecognizer)) + || context.getSystemService(LocationManager.class).isProviderPackage(packageName); + } + /** * Gets the list of packages that have permissions that specified * {@code requestDontAutoRevokePermissions=true} in their * {@code application} manifest declaration. diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index 7e3a0f30e75c..80a3e1693ab1 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -31,40 +31,25 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_W import static android.media.AudioSystem.MODE_IN_COMMUNICATION; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; -import android.Manifest; import android.annotation.NonNull; import android.app.AppOpsManager; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.content.pm.Attribution; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.icu.text.ListFormatter; -import android.location.LocationManager; import android.media.AudioManager; import android.os.Process; import android.os.UserHandle; import android.provider.DeviceConfig; -import android.provider.Settings; -import android.speech.RecognitionService; -import android.speech.RecognizerIntent; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; - -import com.android.internal.R; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; /** * A helper which gets all apps which have used microphone, camera, and possible location @@ -165,7 +150,7 @@ public class PermissionUsageHelper { * Constructor for PermissionUsageHelper * @param context The context from which to derive the package information */ - public PermissionUsageHelper(Context context) { + public PermissionUsageHelper(@NonNull Context context) { mContext = context; mPkgManager = context.getPackageManager(); mAppOpsManager = context.getSystemService(AppOpsManager.class); @@ -180,26 +165,10 @@ public class PermissionUsageHelper { return mUserContexts.get(user); } - // TODO ntmyren: Replace this with better check if this moves beyond teamfood - private boolean isAppPredictor(String packageName, UserHandle user) { - return shouldShowPermissionsHub() && getUserContext(user).getPackageManager() - .checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, packageName) - == PackageManager.PERMISSION_GRANTED; - } - - private boolean isSpeechRecognizerUsage(String op, String packageName) { - if (!OPSTR_RECORD_AUDIO.equals(op)) { - return false; - } - - return packageName.equals( - mContext.getString(R.string.config_systemSpeechRecognizer)); - } - /** * @see PermissionManager.getIndicatorAppOpUsageData */ - public List<PermGroupUsage> getOpUsageData(boolean isMicMuted) { + public @NonNull List<PermGroupUsage> getOpUsageData(boolean isMicMuted) { List<PermGroupUsage> usages = new ArrayList<>(); if (!shouldShowIndicators()) { @@ -215,9 +184,6 @@ public class PermissionUsageHelper { } Map<String, List<OpUsage>> rawUsages = getOpUsages(ops); - Set<List<PackageAttribution>> proxyChains = getProxyChains(rawUsages.get(MICROPHONE)); - Map<PackageAttribution, CharSequence> packagesWithAttributionLabels = - getTrustedAttributions(rawUsages.get(MICROPHONE), proxyChains); ArrayList<String> usedPermGroups = new ArrayList<>(rawUsages.keySet()); @@ -245,15 +211,8 @@ public class PermissionUsageHelper { boolean isPhone = false; String permGroup = usedPermGroups.get(permGroupNum); - Map<PackageAttribution, CharSequence> pkgAttrLabels = packagesWithAttributionLabels; - Set<List<PackageAttribution>> proxies = proxyChains; - if (!MICROPHONE.equals(permGroup)) { - pkgAttrLabels = new ArrayMap<>(); - proxies = new ArraySet<>(); - } - - List<OpUsage> permUsages = removeDuplicatesAndProxies(rawUsages.get(permGroup), - pkgAttrLabels.keySet(), proxies); + ArrayMap<OpUsage, CharSequence> usagesWithLabels = + getUniqueUsagesWithLabels(rawUsages.get(permGroup)); if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) { isPhone = true; @@ -263,11 +222,11 @@ public class PermissionUsageHelper { permGroup = CAMERA; } - for (int usageNum = 0; usageNum < permUsages.size(); usageNum++) { - OpUsage usage = permUsages.get(usageNum); + for (int usageNum = 0; usageNum < usagesWithLabels.size(); usageNum++) { + OpUsage usage = usagesWithLabels.keyAt(usageNum); usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup, usage.lastAccessTime, usage.isRunning, isPhone, - packagesWithAttributionLabels.get(usage.toPackageAttr()))); + usagesWithLabels.valueAt(usageNum))); } } @@ -297,7 +256,7 @@ public class PermissionUsageHelper { long recentThreshold = getRecentThreshold(now); long runningThreshold = getRunningThreshold(now); int opFlags = OP_FLAGS_ALL_TRUSTED; - Map<String, Map<PackageAttribution, OpUsage>> usages = new ArrayMap<>(); + Map<String, Map<Integer, OpUsage>> usages = new ArrayMap<>(); int numPkgOps = ops.size(); for (int pkgOpNum = 0; pkgOpNum < numPkgOps; pkgOpNum++) { @@ -326,9 +285,7 @@ public class PermissionUsageHelper { if (packageName.equals(SYSTEM_PKG) || (!shouldShowPermissionsHub() - && !isUserSensitive(packageName, user, op) - && !isLocationProvider(packageName, user) - && !isSpeechRecognizerUsage(op, packageName))) { + && !isUserSensitive(packageName, user, op))) { continue; } @@ -339,20 +296,20 @@ public class PermissionUsageHelper { AppOpsManager.OpEventProxyInfo proxy = attrOpEntry.getLastProxyInfo(opFlags); if (proxy != null && proxy.getPackageName() != null) { proxyUsage = new OpUsage(proxy.getPackageName(), proxy.getAttributionTag(), - proxy.getUid(), lastAccessTime, isRunning, null); + op, proxy.getUid(), lastAccessTime, isRunning, null); } String permGroupName = getGroupForOp(op); - OpUsage usage = new OpUsage(packageName, attributionTag, uid, + OpUsage usage = new OpUsage(packageName, attributionTag, op, uid, lastAccessTime, isRunning, proxyUsage); - PackageAttribution packageAttr = usage.toPackageAttr(); + Integer packageAttr = usage.getPackageAttrHash(); if (!usages.containsKey(permGroupName)) { - ArrayMap<PackageAttribution, OpUsage> map = new ArrayMap<>(); + ArrayMap<Integer, OpUsage> map = new ArrayMap<>(); map.put(packageAttr, usage); usages.put(permGroupName, map); } else { - Map<PackageAttribution, OpUsage> permGroupUsages = + Map<Integer, OpUsage> permGroupUsages = usages.get(permGroupName); if (!permGroupUsages.containsKey(packageAttr)) { permGroupUsages.put(packageAttr, usage); @@ -374,380 +331,119 @@ public class PermissionUsageHelper { return flattenedUsages; } - /** - * Take the list of all usages, figure out any proxy chains, get all possible special - * attribution labels, and figure out which usages need to show a special label, if any. - * - * @param usages The raw permission usages - * - * @return A map of package + attribution (in the form of a PackageAttribution object) to - * trusted attribution label, if there is one - */ - private ArrayMap<PackageAttribution, CharSequence> getTrustedAttributions( - List<OpUsage> usages, Set<List<PackageAttribution>> proxyChains) { - ArrayMap<PackageAttribution, CharSequence> attributions = new ArrayMap<>(); - if (usages == null) { - return attributions; - } - - Map<PackageAttribution, CharSequence> trustedLabels = - getTrustedAttributionLabels(usages); - - for (List<PackageAttribution> chain : proxyChains) { - // If this chain is empty, or has only one link, then do not show any special labels - if (chain.size() <= 1) { - continue; - } - - // If the last link in the chain is not user sensitive, do not show it. - boolean lastLinkIsUserSensitive = false; - for (int i = 0; i < usages.size(); i++) { - PackageAttribution lastLink = chain.get(chain.size() - 1); - if (lastLink.equals(usages.get(i).toPackageAttr())) { - lastLinkIsUserSensitive = true; - break; - } - } - if (!lastLinkIsUserSensitive) { - continue; - } - - List<CharSequence> labels = new ArrayList<>(); - for (int i = 0; i < chain.size(); i++) { - // If this is the last link in the proxy chain, assign it the series of labels - // Else, if it has a special label, add that label - // Else, if there are no other apps in the remaining part of the chain which - // have the same package name, add the app label - // If it is not the last link in the chain, remove its attribution - PackageAttribution attr = chain.get(i); - CharSequence trustedLabel = trustedLabels.get(attr); - if (i == chain.size() - 1) { - attributions.put(attr, formatLabelList(labels)); - } else if (trustedLabel != null && !labels.contains(trustedLabel)) { - labels.add(trustedLabel); - trustedLabels.remove(attr); - } else { - boolean remainingChainHasPackage = false; - for (int attrNum = i + 1; attrNum < chain.size() - 1; attrNum++) { - if (chain.get(i).packageName.equals(attr.packageName)) { - remainingChainHasPackage = true; - break; - } - } - if (!remainingChainHasPackage) { - try { - ApplicationInfo appInfo = mPkgManager.getApplicationInfoAsUser( - attr.packageName, 0, attr.getUser()); - CharSequence appLabel = appInfo.loadLabel( - getUserContext(attr.getUser()).getPackageManager()); - labels.add(appLabel); - } catch (PackageManager.NameNotFoundException e) { - // Do nothing - } - } - } - } - } - - for (PackageAttribution attr : trustedLabels.keySet()) { - attributions.put(attr, trustedLabels.get(attr)); - } - - return attributions; - } - private CharSequence formatLabelList(List<CharSequence> labels) { return ListFormatter.getInstance().format(labels); } - /** - * Get all chains of proxy usages. A proxy chain is defined as one usage at the root, then - * further proxy usages, where the app and attribution tag of the proxy in the proxy usage - * matches the previous usage in the chain. - * - * @param usages The permission usages - * - * @return A set of lists of package attributions. One list represents a chain of proxy usages, - * with the start of the chain (the usage without a proxy) at position 0, and each usage down - * the chain has the previous one listed as a proxy usage. - */ - private Set<List<PackageAttribution>> getProxyChains(List<OpUsage> usages) { - if (usages == null) { - return new ArraySet<>(); - } + private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(List<OpUsage> usages) { + ArrayMap<OpUsage, CharSequence> usagesAndLabels = new ArrayMap<>(); - ArrayMap<PackageAttribution, ArrayList<PackageAttribution>> proxyChains = new ArrayMap<>(); - // map of usages that still need to be removed, or added to a chain - ArrayMap<PackageAttribution, OpUsage> remainingUsages = new ArrayMap<>(); - // map of usage.proxy -> usage, telling us if a usage is a proxy - ArrayMap<PackageAttribution, PackageAttribution> proxies = new ArrayMap<>(); + if (usages == null) { + return usagesAndLabels; + } + + ArrayMap<Integer, OpUsage> allUsages = new ArrayMap<>(); + // map of uid -> most recent non-proxy-related usage for that uid. + ArrayMap<Integer, OpUsage> mostRecentUsages = new ArrayMap<>(); + // set of all uids involved in a proxy usage + ArraySet<Integer> proxyUids = new ArraySet<>(); + // map of usage -> list of proxy app labels + ArrayMap<OpUsage, ArrayList<CharSequence>> proxyLabels = new ArrayMap<>(); + // map of usage.proxy hash -> usage hash, telling us if a usage is a proxy + ArrayMap<Integer, OpUsage> proxies = new ArrayMap<>(); for (int i = 0; i < usages.size(); i++) { OpUsage usage = usages.get(i); - remainingUsages.put(usage.toPackageAttr(), usage); + allUsages.put(usage.getPackageAttrHash(), usage); if (usage.proxy != null) { - proxies.put(usage.proxy.toPackageAttr(), usage.toPackageAttr()); + proxies.put(usage.proxy.getPackageAttrHash(), usage); } } - // find all possible end points for chains - List<PackageAttribution> keys = new ArrayList<>(remainingUsages.keySet()); - for (int usageNum = 0; usageNum < remainingUsages.size(); usageNum++) { - OpUsage usage = remainingUsages.get(keys.get(usageNum)); + // find all possible end points for chains, and find the most recent of the rest of the uses + for (int usageNum = 0; usageNum < usages.size(); usageNum++) { + OpUsage usage = usages.get(usageNum); if (usage == null) { continue; } - PackageAttribution usageAttr = usage.toPackageAttr(); + + int usageAttr = usage.getPackageAttrHash(); // If this usage has a proxy, but is not a proxy, it is the end of a chain. - // If it has no proxy, and isn't a proxy, remove it. if (!proxies.containsKey(usageAttr) && usage.proxy != null) { - ArrayList<PackageAttribution> proxyList = new ArrayList<>(); - proxyList.add(usageAttr); - proxyChains.put(usageAttr, proxyList); - } else if (!proxies.containsKey(usageAttr) && usage.proxy == null) { - remainingUsages.remove(keys.get(usageNum)); + proxyLabels.put(usage, new ArrayList<>()); + proxyUids.add(usage.uid); + } + if (!mostRecentUsages.containsKey(usage.uid) || usage.lastAccessTime + > mostRecentUsages.get(usage.uid).lastAccessTime) { + mostRecentUsages.put(usage.uid, usage); } } - // assemble the chains in reverse order, then invert them - for (int numStart = 0; numStart < proxyChains.size(); numStart++) { - PackageAttribution currPackageAttr = proxyChains.keyAt(numStart); - ArrayList<PackageAttribution> proxyChain = proxyChains.get(currPackageAttr); - OpUsage currentUsage = remainingUsages.get(currPackageAttr); - if (currentUsage == null || proxyChain == null) { + // get all the proxy labels + for (int numStart = 0; numStart < proxyLabels.size(); numStart++) { + OpUsage start = proxyLabels.keyAt(numStart); + // Remove any non-proxy usage for the starting uid + mostRecentUsages.remove(start.uid); + OpUsage currentUsage = proxyLabels.keyAt(numStart); + ArrayList<CharSequence> proxyLabelList = proxyLabels.get(currentUsage); + if (currentUsage == null || proxyLabelList == null) { continue; } + int iterNum = 0; + int maxUsages = allUsages.size(); while (currentUsage.proxy != null) { - currPackageAttr = currentUsage.proxy.toPackageAttr(); - currentUsage = remainingUsages.get(currPackageAttr); - - boolean invalidState = false; - for (int chainNum = 0; chainNum < proxyChain.size(); chainNum++) { - if (currentUsage == null || proxyChain.get(chainNum).equals(currPackageAttr)) { - // either our current value is not in the usage list, or we have a cycle - invalidState = true; - break; - } - } - - if (invalidState) { - break; - } - - proxyChain.add(currPackageAttr); - } - // invert the lists, so the element without a proxy is first on the list - Collections.reverse(proxyChain); - } - return new ArraySet<>(proxyChains.values()); - } - - /** - * Gets all trusted proxied voice IME and voice recognition microphone uses, and get the - * label needed to display with it, as well as information about the proxy whose label is being - * shown, if applicable. - * - * @param usages The permission usages - * - * @return A map of package attribution -> the attribution label for that package attribution, - * if applicable - */ - private Map<PackageAttribution, CharSequence> getTrustedAttributionLabels( - List<OpUsage> usages) { - List<UserHandle> users = new ArrayList<>(); - for (int i = 0; i < usages.size(); i++) { - UserHandle user = UserHandle.getUserHandleForUid(usages.get(i).uid); - if (!users.contains(user)) { - users.add(user); - } - } - - Map<PackageAttribution, CharSequence> trustedLabels = new ArrayMap<>(); - for (int userNum = 0; userNum < users.size(); userNum++) { - UserHandle user = users.get(userNum); - Context userContext = mContext.createContextAsUser(user, 0); - - // Get all voice IME labels - Map<String, CharSequence> voiceInputs = new ArrayMap<>(); - List<InputMethodInfo> inputs = userContext.getSystemService(InputMethodManager.class) - .getEnabledInputMethodList(); - for (int inputNum = 0; inputNum < inputs.size(); inputNum++) { - InputMethodInfo input = inputs.get(inputNum); - for (int subtypeNum = 0; subtypeNum < input.getSubtypeCount(); subtypeNum++) { - if (VOICE_IME_SUBTYPE.equals(input.getSubtypeAt(subtypeNum).getMode())) { - voiceInputs.put(input.getPackageName(), input.getServiceInfo() - .loadUnsafeLabel(userContext.getPackageManager())); + if (allUsages.containsKey(currentUsage.proxy.getPackageAttrHash())) { + currentUsage = allUsages.get(currentUsage.proxy.getPackageAttrHash()); + } else { + // We are missing the proxy usage. This may be because it's a one-step trusted + // proxy. Check if we should show the proxy label, and show it, if so. + OpUsage proxy = currentUsage.proxy; + if (PermissionManager.isSpecialCaseShownIndicator(mContext, proxy.packageName) + || isUserSensitive(proxy.packageName, proxy.getUser(), proxy.op)) { + currentUsage = proxy; + // We've effectively added one usage, so increment the max number of usages + maxUsages++; + } else { break; } } - } - // Get the currently selected recognizer from the secure setting - String recognitionPackageName = Settings.Secure.getString( - userContext.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE); - if (recognitionPackageName == null) { - continue; - } - recognitionPackageName = - ComponentName.unflattenFromString(recognitionPackageName).getPackageName(); - Map<String, CharSequence> recognizers = new ArrayMap<>(); - List<ResolveInfo> availableRecognizers = mPkgManager.queryIntentServicesAsUser( - new Intent(RecognitionService.SERVICE_INTERFACE), PackageManager.GET_META_DATA, - user.getIdentifier()); - for (int recogNum = 0; recogNum < availableRecognizers.size(); recogNum++) { - ResolveInfo info = availableRecognizers.get(recogNum); - if (recognitionPackageName.equals(info.serviceInfo.packageName)) { - recognizers.put(recognitionPackageName, info.serviceInfo.loadUnsafeLabel( - userContext.getPackageManager())); - } - } - Map<String, CharSequence> recognizerIntents = new ArrayMap<>(); - List<ResolveInfo> availableRecognizerIntents = mPkgManager.queryIntentActivitiesAsUser( - new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), - PackageManager.GET_META_DATA, user); - for (int recogNum = 0; recogNum < availableRecognizerIntents.size(); recogNum++) { - ResolveInfo info = availableRecognizerIntents.get(recogNum); - if (info.activityInfo == null) { - continue; - } - String pkgName = info.activityInfo.packageName; - if (recognitionPackageName.equals(pkgName) && recognizers.containsKey(pkgName)) { - recognizerIntents.put(pkgName, recognizers.get(pkgName)); - } - } - for (int usageNum = 0; usageNum < usages.size(); usageNum++) { - setTrustedAttrsForAccess(usages.get(usageNum), user, false, voiceInputs, - trustedLabels); - setTrustedAttrsForAccess(usages.get(usageNum), user, false, recognizerIntents, - trustedLabels); - setTrustedAttrsForAccess(usages.get(usageNum), user, true, recognizers, - trustedLabels); - } - } - - return trustedLabels; - } - - private void setTrustedAttrsForAccess(OpUsage opUsage, UserHandle currUser, boolean getProxy, - Map<String, CharSequence> trustedMap, Map<PackageAttribution, CharSequence> toSetMap) { - OpUsage usage = opUsage; - if (getProxy) { - usage = opUsage.proxy; - } - - if (usage == null || !usage.getUser().equals(currUser) - || !trustedMap.containsKey(usage.packageName)) { - return; - } - - CharSequence label = getAttributionLabel(usage); - if (trustedMap.get(usage.packageName).equals(label)) { - toSetMap.put(opUsage.toPackageAttr(), label); - } - } - - private CharSequence getAttributionLabel(OpUsage usage) { - if (usage.attributionTag == null) { - return null; - } - - PackageInfo pkgInfo; - try { - pkgInfo = mPkgManager.getPackageInfoAsUser(usage.packageName, - PackageManager.GET_ATTRIBUTIONS, usage.getUser().getIdentifier()); - if (pkgInfo.attributions == null || pkgInfo.attributions.length == 0) { - return null; - } - for (int attrNum = 0; attrNum < pkgInfo.attributions.length; attrNum++) { - Attribution attr = pkgInfo.attributions[attrNum]; - if (usage.attributionTag.equals(attr.getTag())) { - return mContext.createPackageContextAsUser(usage.packageName, 0, - usage.getUser()).getString(attr.getLabel()); - } - } - return null; - } catch (PackageManager.NameNotFoundException e) { - return null; - } - } - - /** - * If we have multiple usages of a - * @param rawUsages The list of all usages that we wish to - * @param specialAttributions A set of all usages that have a special label - * @param proxies A list of proxy chains- all links but the last on the chain should be removed, - * if the last link has a special label - * @return A list of usages without duplicates or proxy usages. - */ - private List<OpUsage> removeDuplicatesAndProxies(List<OpUsage> rawUsages, - Set<PackageAttribution> specialAttributions, - Set<List<PackageAttribution>> proxies) { - List<OpUsage> deDuped = new ArrayList<>(); - if (rawUsages == null) { - return deDuped; - } - - List<PackageAttribution> toRemoveProxies = new ArrayList<>(); - for (List<PackageAttribution> proxyList: proxies) { - PackageAttribution lastLink = proxyList.get(proxyList.size() - 1); - if (!specialAttributions.contains(lastLink)) { - continue; - } - for (int proxyNum = 0; proxyNum < proxyList.size(); proxyNum++) { - if (!proxyList.get(proxyNum).equals(lastLink)) { - toRemoveProxies.add(proxyList.get(proxyNum)); - } - } - } - - for (int usageNum = 0; usageNum < rawUsages.size(); usageNum++) { - OpUsage usage = rawUsages.get(usageNum); - - // If this attribution is a proxy, remove it - if (toRemoveProxies.contains(usage.toPackageAttr())) { - continue; - } - - // If this attribution has a special attribution, do not remove it - if (specialAttributions.contains(usage.toPackageAttr())) { - deDuped.add(usage); - continue; - } - - - // Search the rest of the list for usages with the same UID. If this is the most recent - // usage for that uid, keep it. Otherwise, remove it - boolean isMostRecentForUid = true; - for (int otherUsageNum = 0; otherUsageNum < rawUsages.size(); otherUsageNum++) { - // Do not compare this usage to itself - if (otherUsageNum == usageNum) { - continue; + if (currentUsage == null || iterNum == maxUsages + || currentUsage.getPackageAttrHash() == start.getPackageAttrHash()) { + // We have an invalid state, or a cycle, so break + break; } - OpUsage otherUsage = rawUsages.get(otherUsageNum); - if (otherUsage.uid == usage.uid) { - if (otherUsage.isRunning && !usage.isRunning) { - isMostRecentForUid = false; - } else if (usage.isRunning - && otherUsage.lastAccessTime >= usage.lastAccessTime) { - isMostRecentForUid = false; - } else if (otherUsage.lastAccessTime >= usage.lastAccessTime) { - isMostRecentForUid = false; - } - - if (!isMostRecentForUid) { - break; + proxyUids.add(currentUsage.uid); + try { + PackageManager userPkgManager = + getUserContext(currentUsage.getUser()).getPackageManager(); + ApplicationInfo appInfo = userPkgManager.getApplicationInfo( + currentUsage.packageName, 0); + CharSequence appLabel = appInfo.loadLabel(userPkgManager); + // If we don't already have the app label, and it's not the same as the main + // app, add it + if (!proxyLabelList.contains(appLabel) + && !currentUsage.packageName.equals(start.packageName)) { + proxyLabelList.add(appLabel); } + } catch (PackageManager.NameNotFoundException e) { + // Ignore } + iterNum++; } + usagesAndLabels.put(start, + proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList)); + } - if (isMostRecentForUid) { - deDuped.add(usage); + for (int uid : mostRecentUsages.keySet()) { + if (!proxyUids.contains(uid)) { + usagesAndLabels.put(mostRecentUsages.get(uid), null); } } - return deDuped; + return usagesAndLabels; } private boolean isUserSensitive(String packageName, UserHandle user, String op) { @@ -763,11 +459,6 @@ public class PermissionUsageHelper { return (permFlags & FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0; } - private boolean isLocationProvider(String packageName, UserHandle user) { - return getUserContext(user) - .getSystemService(LocationManager.class).isProviderPackage(packageName); - } - /** * Represents the usage of an App op by a particular package and attribution */ @@ -775,62 +466,45 @@ public class PermissionUsageHelper { public final String packageName; public final String attributionTag; + public final String op; public final int uid; public final long lastAccessTime; public final OpUsage proxy; public final boolean isRunning; - OpUsage(String packageName, String attributionTag, int uid, long lastAccessTime, + OpUsage(String packageName, String attributionTag, String op, int uid, long lastAccessTime, boolean isRunning, OpUsage proxy) { - this.isRunning = isRunning; this.packageName = packageName; this.attributionTag = attributionTag; + this.op = op; this.uid = uid; this.lastAccessTime = lastAccessTime; + this.isRunning = isRunning; this.proxy = proxy; } - public PackageAttribution toPackageAttr() { - return new PackageAttribution(packageName, attributionTag, uid); - } - public UserHandle getUser() { return UserHandle.getUserHandleForUid(uid); } - } - /** - * A unique identifier for one package attribution, made up of attribution tag, package name - * and user - */ - private static class PackageAttribution { - public final String packageName; - public final String attributionTag; - public final int uid; + public int getPackageAttrHash() { + return Objects.hash(packageName, attributionTag, uid); + } - PackageAttribution(String packageName, String attributionTag, int uid) { - this.packageName = packageName; - this.attributionTag = attributionTag; - this.uid = uid; + @Override + public int hashCode() { + return Objects.hash(packageName, attributionTag, op, uid, lastAccessTime, isRunning); } @Override public boolean equals(Object obj) { - if (!(obj instanceof PackageAttribution)) { + if (!(obj instanceof OpUsage)) { return false; } - PackageAttribution other = (PackageAttribution) obj; + OpUsage other = (OpUsage) obj; return Objects.equals(packageName, other.packageName) && Objects.equals(attributionTag, - other.attributionTag) && Objects.equals(uid, other.uid); - } - - @Override - public int hashCode() { - return Objects.hash(packageName, attributionTag, uid); - } - - public UserHandle getUser() { - return UserHandle.getUserHandleForUid(uid); + other.attributionTag) && Objects.equals(op, other.op) && uid == other.uid + && lastAccessTime == other.lastAccessTime && isRunning == other.isRunning; } } } diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java index d71a8300d9b8..a9348c68165d 100644 --- a/core/java/android/service/timezone/TimeZoneProviderService.java +++ b/core/java/android/service/timezone/TimeZoneProviderService.java @@ -218,7 +218,30 @@ public abstract class TimeZoneProviderService extends Service { } /** - * Starts the provider sending updates. + * Informs the provider that it should start detecting and reporting the detected time zone + * state via the various {@code report} methods. Implementations of {@link + * #onStartUpdates(long)} should return immediately, and will typically be used to start + * worker threads or begin asynchronous location listening. + * + * <p>Between {@link #onStartUpdates(long)} and {@link #onStopUpdates()} calls, the Android + * system server holds the latest report from the provider in memory. After an initial report, + * provider implementations are only required to send a report via {@link + * #reportSuggestion(TimeZoneProviderSuggestion)} or via {@link #reportUncertain()} when it + * differs from the previous report. + * + * <p>{@link #reportPermanentFailure(Throwable)} can also be called by provider implementations + * in rare cases, after which the provider should consider itself stopped and not make any + * further reports. {@link #onStopUpdates()} will not be called in this case. + * + * <p>The {@code initializationTimeoutMillis} parameter indicates how long the provider has been + * granted to call one of the {@code report} methods for the first time. If the provider does + * not call one of the {@code report} methods in this time, it may be judged uncertain and the + * Android system server may move on to use other providers or detection methods. Providers + * should therefore make best efforts during this time to generate a report, which could involve + * increased power usage. Providers should preferably report an explicit {@link + * #reportUncertain()} if the time zone(s) cannot be detected within the initialization timeout. + * + * @see #onStopUpdates() for the signal from the system server to stop sending reports */ public abstract void onStartUpdates(@DurationMillisLong long initializationTimeoutMillis); @@ -228,7 +251,8 @@ public abstract class TimeZoneProviderService extends Service { } /** - * Stops the provider sending updates. + * Stops the provider sending further updates. This will be called after {@link + * #onStartUpdates(long)}. */ public abstract void onStopUpdates(); diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index e9a79e70fd74..d47ae2783336 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -1261,6 +1261,8 @@ public class PhoneStateListener { // 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. @@ -1579,6 +1581,11 @@ public class PhoneStateListener { public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) { // default implementation empty } + + public void onLinkCapacityEstimateChanged( + List<LinkCapacityEstimate> linkCapacityEstimateList) { + // default implementation empty + } } private void log(String s) { diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index e3d3dec60151..a0875a275ec6 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -565,6 +565,21 @@ public class TelephonyCallback { @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; + + /** + * Event for changes to the link capacity estimate (LCE) + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * + * @see LinkCapacityEstimateChangedListener#onLinkCapacityEstimateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37; + + /** * @hide */ @@ -604,7 +619,8 @@ public class TelephonyCallback { EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, EVENT_DATA_ENABLED_CHANGED, EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED, - EVENT_LEGACY_CALL_STATE_CHANGED + EVENT_LEGACY_CALL_STATE_CHANGED, + EVENT_LINK_CAPACITY_ESTIMATE_CHANGED }) @Retention(RetentionPolicy.SOURCE) public @interface TelephonyEvent { @@ -1370,6 +1386,25 @@ public class TelephonyCallback { @TelephonyManager.DataEnabledReason int reason); } + /** + * Interface for link capacity estimate changed listener. + * + * @hide + */ + @SystemApi + public interface LinkCapacityEstimateChangedListener { + /** + * Callback invoked when the link capacity estimate (LCE) changes + * + * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate} + * The list size is at least 1. + * In case of a dual connected network, the list size could be 2. + * Use {@link LinkCapacityEstimate#getType()} to get the type of each element. + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + void onLinkCapacityEstimateChanged( + @NonNull List<LinkCapacityEstimate> linkCapacityEstimateList); + } /** * The callback methods need to be called on the handler thread where @@ -1718,5 +1753,16 @@ public class TelephonyCallback { () -> listener.onAllowedNetworkTypesChanged(reason, allowedNetworkType))); } + + public void onLinkCapacityEstimateChanged( + List<LinkCapacityEstimate> linkCapacityEstimateList) { + LinkCapacityEstimateChangedListener listener = + (LinkCapacityEstimateChangedListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onLinkCapacityEstimateChanged( + linkCapacityEstimateList))); + } } } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 3fa63d8c1a9c..1ec12fe12b36 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -842,9 +842,23 @@ public class TelephonyRegistryManager { } } + /** + * Notify that the link capacity estimate has changed. + * @param slotIndex for the phone object that gets the updated link capacity estimate + * @param subId for subscription that gets the updated link capacity estimate + * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate} + */ + public void notifyLinkCapacityEstimateChanged(int slotIndex, int subId, + List<LinkCapacityEstimate> linkCapacityEstimateList) { + try { + sRegistry.notifyLinkCapacityEstimateChanged(slotIndex, subId, linkCapacityEstimateList); + } catch (RemoteException ex) { + // system server crash + } + } + public @NonNull Set<Integer> getEventsFromCallback( @NonNull TelephonyCallback telephonyCallback) { - Set<Integer> eventList = new ArraySet<>(); if (telephonyCallback instanceof TelephonyCallback.ServiceStateListener) { @@ -976,6 +990,10 @@ public class TelephonyRegistryManager { eventList.add(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED); } + if (telephonyCallback instanceof TelephonyCallback.LinkCapacityEstimateChangedListener) { + eventList.add(TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED); + } + return eventList; } diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index ddb49786dce6..10721ad30525 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -43,8 +43,11 @@ interface IRecentsAnimationController { * accordingly. This should be called before `finish` * @param taskId for which the leash should be updated * @param destinationBounds bounds of the final PiP window + * @param windowCrop bounds to crop as part of final transform. + * @param float9 An array of 9 floats to be used as matrix transform. */ - void setFinishTaskBounds(int taskId, in Rect destinationBounds); + void setFinishTaskBounds(int taskId, in Rect destinationBounds, in Rect windowCrop, + in float[] float9); /** * Notifies to the system that the animation into Recents should end, and all leashes associated diff --git a/core/java/android/view/IScrollCaptureCallbacks.aidl b/core/java/android/view/IScrollCaptureCallbacks.aidl index 26eaac0a2bf5..9b3561400efc 100644 --- a/core/java/android/view/IScrollCaptureCallbacks.aidl +++ b/core/java/android/view/IScrollCaptureCallbacks.aidl @@ -27,13 +27,6 @@ import android.view.Surface; */ interface IScrollCaptureCallbacks { /** - * Provides the result of WindowManagerService#requestScrollCapture - * - * @param response the response which describes the result - */ - oneway void onScrollCaptureResponse(in ScrollCaptureResponse response); - - /** * Called in reply to IScrollCaptureConnection#startCapture, when the remote end has confirmed * the request and is ready to begin capturing images. */ diff --git a/core/java/android/view/IScrollCaptureConnection.aidl b/core/java/android/view/IScrollCaptureConnection.aidl index c55e88800393..3a6b69397919 100644 --- a/core/java/android/view/IScrollCaptureConnection.aidl +++ b/core/java/android/view/IScrollCaptureConnection.aidl @@ -18,6 +18,7 @@ package android.view; import android.graphics.Rect; import android.os.ICancellationSignal; +import android.view.IScrollCaptureCallbacks; import android.view.Surface; @@ -31,11 +32,12 @@ interface IScrollCaptureConnection { /** * Informs the target that it has been selected for scroll capture. * - * @param surface a return channel for image buffers + * @param surface used to shuttle image buffers between processes + * @param callbacks a return channel for requests * - * @return a cancallation signal which is used cancel the request + * @return a cancallation signal which is used cancel the start request */ - ICancellationSignal startCapture(in Surface surface); + ICancellationSignal startCapture(in Surface surface, IScrollCaptureCallbacks callbacks); /** * Request the target capture an image within the provided rectangle. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java b/core/java/android/view/IScrollCaptureResponseListener.aidl index 30f535ba940c..7220f6ca5661 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java +++ b/core/java/android/view/IScrollCaptureResponseListener.aidl @@ -14,21 +14,22 @@ * limitations under the License. */ -package com.android.wm.shell.common; +package android.view; -import android.content.Context; +import android.graphics.Rect; +import android.view.ScrollCaptureResponse; +import android.view.Surface; /** - * An interface for controllers that can receive remote calls. + * Asynchronous callback channel for the initial response to a scroll capture request. + * + * {@hide} */ -public interface RemoteCallable<T> { - /** - * Returns a context used for permission checking. - */ - Context getContext(); - +interface IScrollCaptureResponseListener { /** - * Returns the executor to post the handler callback to. + * Provides the initial response to a scroll capture request. + * + * @param response the response which describes the result */ - ShellExecutor getRemoteCallExecutor(); -}
\ No newline at end of file + oneway void onScrollCaptureResponse(in ScrollCaptureResponse response); +} diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index fb012ebbc3db..8d59ba0b1f76 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -26,7 +26,7 @@ import android.view.DisplayCutout; import android.view.DragEvent; import android.view.InsetsSourceControl; import android.view.InsetsState; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.KeyEvent; import android.view.MotionEvent; import android.window.ClientWindowFrames; @@ -134,5 +134,5 @@ oneway interface IWindow { * * @param callbacks to receive responses */ - void requestScrollCapture(in IScrollCaptureCallbacks callbacks); + void requestScrollCapture(in IScrollCaptureResponseListener callbacks); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index b345b2e58252..a42126f18357 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -42,7 +42,7 @@ import android.view.IDisplayFoldListener; import android.view.IDisplayWindowRotationController; import android.view.IOnKeyguardExitResult; import android.view.IPinnedTaskListener; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.RemoteAnimationAdapter; import android.view.IRotationWatcher; import android.view.ISystemGestureExclusionListener; @@ -744,10 +744,10 @@ interface IWindowManager * @param behindClient token for a window, used to filter the search to windows behind it, or * {@code null} to accept a window at any zOrder * @param taskId specifies the id of a task the result must belong to, or -1 to ignore task ids - * @param callbacks the object to receive replies + * @param listener the object to receive the response */ void requestScrollCapture(int displayId, IBinder behindClient, int taskId, - IScrollCaptureCallbacks callbacks); + IScrollCaptureResponseListener listener); /** * Holds the WM lock for the specified amount of milliseconds. diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java index 3456e016c42c..a6d786e1db21 100644 --- a/core/java/android/view/ScrollCaptureConnection.java +++ b/core/java/android/view/ScrollCaptureConnection.java @@ -32,6 +32,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -50,8 +51,9 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { private final Object mLock = new Object(); private final Rect mScrollBounds; private final Point mPositionInWindow; - private final CloseGuard mCloseGuard; private final Executor mUiThread; + private final CloseGuard mCloseGuard = new CloseGuard(); + private ScrollCaptureCallback mLocal; private IScrollCaptureCallbacks mRemote; @@ -60,42 +62,38 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { private CancellationSignal mCancellation; - private volatile boolean mStarted; - private volatile boolean mConnected; + private volatile boolean mActive; /** * Constructs a ScrollCaptureConnection. * + * @param uiThread an executor for the UI thread of the containing View * @param selectedTarget the target the client is controlling - * @param remote the callbacks to reply to system requests * * @hide */ public ScrollCaptureConnection( @NonNull Executor uiThread, - @NonNull ScrollCaptureTarget selectedTarget, - @NonNull IScrollCaptureCallbacks remote) { + @NonNull ScrollCaptureTarget selectedTarget) { mUiThread = requireNonNull(uiThread, "<uiThread> must non-null"); requireNonNull(selectedTarget, "<selectedTarget> must non-null"); - mRemote = requireNonNull(remote, "<callbacks> must non-null"); mScrollBounds = requireNonNull(Rect.copyOrNull(selectedTarget.getScrollBounds()), "target.getScrollBounds() must be non-null to construct a client"); - mLocal = selectedTarget.getCallback(); mPositionInWindow = new Point(selectedTarget.getPositionInWindow()); - - mCloseGuard = new CloseGuard(); - mCloseGuard.open("close"); - mConnected = true; } @BinderThread @Override - public ICancellationSignal startCapture(Surface surface) throws RemoteException { - checkConnected(); + public ICancellationSignal startCapture(@NonNull Surface surface, + @NonNull IScrollCaptureCallbacks remote) throws RemoteException { + + mCloseGuard.open("close"); + if (!surface.isValid()) { throw new RemoteException(new IllegalArgumentException("surface must be valid")); } + mRemote = requireNonNull(remote, "<callbacks> must non-null"); ICancellationSignal cancellation = CancellationSignal.createTransport(); mCancellation = CancellationSignal.fromTransport(cancellation); @@ -110,7 +108,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { @UiThread private void onStartCaptureCompleted() { - mStarted = true; + mActive = true; try { mRemote.onCaptureStarted(); } catch (RemoteException e) { @@ -119,13 +117,11 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { } } - @BinderThread @Override public ICancellationSignal requestImage(Rect requestRect) throws RemoteException { Trace.beginSection("requestImage"); - checkConnected(); - checkStarted(); + checkActive(); ICancellationSignal cancellation = CancellationSignal.createTransport(); mCancellation = CancellationSignal.fromTransport(cancellation); @@ -152,8 +148,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { @BinderThread @Override public ICancellationSignal endCapture() throws RemoteException { - checkConnected(); - checkStarted(); + checkActive(); ICancellationSignal cancellation = CancellationSignal.createTransport(); mCancellation = CancellationSignal.fromTransport(cancellation); @@ -167,64 +162,48 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { @UiThread private void onEndCaptureCompleted() { - synchronized (mLock) { - mStarted = false; - try { + mActive = false; + try { + if (mRemote != null) { mRemote.onCaptureEnded(); - } catch (RemoteException e) { - Log.w(TAG, "Shutting down due to error: ", e); - close(); } + } catch (RemoteException e) { + Log.w(TAG, "Caught exception confirming capture end!", e); + } finally { + close(); } } @BinderThread @Override public void close() { - if (mStarted) { - Log.w(TAG, "close(): capture is still started?! Ending now."); - + if (mActive) { + if (mCancellation != null) { + Log.w(TAG, "close(): cancelling pending operation."); + mCancellation.cancel(); + mCancellation = null; + } + Log.w(TAG, "close(): capture session still active! Ending now."); // -> UiThread mUiThread.execute(() -> mLocal.onScrollCaptureEnd(() -> { /* ignore */ })); - mStarted = false; + mActive = false; } - disconnect(); + mActive = false; + mSession = null; + mRemote = null; + mLocal = null; + mCloseGuard.close(); + Reference.reachabilityFence(this); } - /** - * Shuts down this client and releases references to dependent objects. No attempt is made - * to notify the controller, use with caution! - */ - private void disconnect() { - synchronized (mLock) { - mSession = null; - mConnected = false; - mStarted = false; - mRemote = null; - mLocal = null; - mCloseGuard.close(); - } - } - - public boolean isConnected() { - return mConnected; - } - - public boolean isStarted() { - return mStarted; - } - - private synchronized void checkConnected() throws RemoteException { - synchronized (mLock) { - if (!mConnected) { - throw new RemoteException(new IllegalStateException("Not connected")); - } - } + @VisibleForTesting + public boolean isActive() { + return mActive; } - private void checkStarted() throws RemoteException { + private void checkActive() throws RemoteException { synchronized (mLock) { - if (!mStarted) { + if (!mActive) { throw new RemoteException(new IllegalStateException("Not started!")); } } @@ -233,24 +212,16 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { /** @return a string representation of the state of this client */ public String toString() { return "ScrollCaptureConnection{" - + "connected=" + mConnected - + ", started=" + mStarted + + "active=" + mActive + ", session=" + mSession + ", remote=" + mRemote + ", local=" + mLocal + "}"; } - @VisibleForTesting - public CancellationSignal getCancellation() { - return mCancellation; - } - protected void finalize() throws Throwable { try { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } + mCloseGuard.warnIfOpen(); close(); } finally { super.finalize(); diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java index 564113edb3c7..8808827b248a 100644 --- a/core/java/android/view/ScrollCaptureResponse.java +++ b/core/java/android/view/ScrollCaptureResponse.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import android.os.Parcelable; +import android.os.RemoteException; import com.android.internal.util.DataClass; @@ -57,11 +58,22 @@ public class ScrollCaptureResponse implements Parcelable { @DataClass.PluralOf("message") private ArrayList<String> mMessages = new ArrayList<>(); - /** Whether a connection has been returned. */ + /** Whether an active connection is present. */ public boolean isConnected() { - return mConnection != null; + return mConnection != null && mConnection.asBinder().isBinderAlive(); } + /** Closes a connection returned with this response. */ + public void close() { + if (mConnection != null) { + try { + mConnection.close(); + } catch (RemoteException e) { + // Ignore + } + mConnection = null; + } + } @@ -367,10 +379,10 @@ public class ScrollCaptureResponse implements Parcelable { } @DataClass.Generated( - time = 1612282689462L, + time = 1614833185795L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java", - inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)") + inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\npublic void close()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 03dd10050724..0167147a1067 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -200,7 +200,8 @@ public final class SurfaceControl implements Parcelable { private static native void nativeSyncInputWindows(long transactionObj); private static native boolean nativeGetDisplayBrightnessSupport(IBinder displayToken); private static native boolean nativeSetDisplayBrightness(IBinder displayToken, - float brightness); + float sdrBrightness, float sdrBrightnessNits, float displayBrightness, + float displayBrightnessNits); private static native long nativeReadTransactionFromParcel(Parcel in); private static native void nativeWriteTransactionToParcel(long nativeObject, Parcel out); private static native void nativeSetShadowRadius(long transactionObj, long nativeObject, @@ -2405,13 +2406,50 @@ public final class SurfaceControl implements Parcelable { * @hide */ public static boolean setDisplayBrightness(IBinder displayToken, float brightness) { + return setDisplayBrightness(displayToken, brightness, -1, brightness, -1); + } + + /** + * Sets the brightness of a display. + * + * @param displayToken + * The token for the display whose brightness is set. + * @param sdrBrightness + * A number between 0.0f (minimum brightness) and 1.0f (maximum brightness), or -1.0f to + * turn the backlight off. Specifies the desired brightness of SDR content. + * @param sdrBrightnessNits + * The value of sdrBrightness converted to calibrated nits. -1 if this isn't available. + * @param displayBrightness + * A number between 0.0f (minimum brightness) and 1.0f (maximum brightness), or + * -1.0f to turn the backlight off. Specifies the desired brightness of the display itself, + * used directly for HDR content. + * @param displayBrightnessNits + * The value of displayBrightness converted to calibrated nits. -1 if this isn't + * available. + * + * @return Whether the method succeeded or not. + * + * @throws IllegalArgumentException if: + * - displayToken is null; + * - brightness is NaN or greater than 1.0f. + * + * @hide + */ + public static boolean setDisplayBrightness(IBinder displayToken, float sdrBrightness, + float sdrBrightnessNits, float displayBrightness, float displayBrightnessNits) { Objects.requireNonNull(displayToken); - if (Float.isNaN(brightness) || brightness > 1.0f - || (brightness < 0.0f && brightness != -1.0f)) { - throw new IllegalArgumentException("brightness must be a number between 0.0f and 1.0f," - + " or -1 to turn the backlight off: " + brightness); - } - return nativeSetDisplayBrightness(displayToken, brightness); + if (Float.isNaN(displayBrightness) || displayBrightness > 1.0f + || (displayBrightness < 0.0f && displayBrightness != -1.0f)) { + throw new IllegalArgumentException("displayBrightness must be a number between 0.0f " + + " and 1.0f, or -1 to turn the backlight off: " + displayBrightness); + } + if (Float.isNaN(sdrBrightness) || sdrBrightness > 1.0f + || (sdrBrightness < 0.0f && sdrBrightness != -1.0f)) { + throw new IllegalArgumentException("sdrBrightness must be a number between 0.0f " + + "and 1.0f, or -1 to turn the backlight off: " + displayBrightness); + } + return nativeSetDisplayBrightness(displayToken, sdrBrightness, sdrBrightnessNits, + displayBrightness, displayBrightnessNits); } /** diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 9688c677b900..3ffe0c660f9a 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1390,14 +1390,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall viewRoot.mergeWithNextTransaction(mRtTransaction, frameNumber); return; } - - // Otherwise if the if the ViewRoot is not null, use deferred transaction instead. - if (frameNumber > 0 && viewRoot != null && viewRoot.mSurface.isValid() - && mSurfaceControl != null) { - mRtTransaction.deferTransactionUntil(mSurfaceControl, - viewRoot.getSurfaceControl(), frameNumber); - } - mRtTransaction.apply(); } private Rect mRTLastReportedPosition = new Rect(); @@ -1470,12 +1462,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall */ synchronized (mSurfaceControlLock) { final ViewRootImpl viewRoot = getViewRootImpl(); - boolean deferTransaction = frameNumber > 0 && viewRoot != null - && viewRoot.mSurface.isValid() && !useBLASTSync(viewRoot); - if (deferTransaction) { - mRtTransaction.deferTransactionUntil(mSurfaceControl, - viewRoot.getSurfaceControl(), frameNumber); - } mRtTransaction.hide(mSurfaceControl); if (mRtReleaseSurfaces) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 35726c0c1f04..d462f5844a70 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5356,7 +5356,7 @@ public final class ViewRootImpl implements ViewParent, updateLocationInParentDisplay(msg.arg1, msg.arg2); } break; case MSG_REQUEST_SCROLL_CAPTURE: - handleScrollCaptureRequest((IScrollCaptureCallbacks) msg.obj); + handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj); break; } } @@ -9267,10 +9267,10 @@ public final class ViewRootImpl implements ViewParent, /** * Dispatches a scroll capture request to the view hierarchy on the ui thread. * - * @param callbacks for replies + * @param listener for the response */ - public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { - mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, callbacks).sendToTarget(); + public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureResponseListener listener) { + mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget(); } /** @@ -9317,10 +9317,10 @@ public final class ViewRootImpl implements ViewParent, * A call to {@link IScrollCaptureCallbacks#onScrollCaptureResponse(ScrollCaptureResponse)} * will follow. * - * @param callbacks to receive responses + * @param listener to receive responses * @see ScrollCaptureTargetSelector */ - public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { + public void handleScrollCaptureRequest(@NonNull IScrollCaptureResponseListener listener) { ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mContext.getMainExecutor()); @@ -9335,7 +9335,7 @@ public final class ViewRootImpl implements ViewParent, getChildVisibleRect(rootView, rect, point); rootView.dispatchScrollCaptureSearch(rect, point, results::addTarget); } - Runnable onComplete = () -> dispatchScrollCaptureSearchResult(callbacks, results); + Runnable onComplete = () -> dispatchScrollCaptureSearchResponse(listener, results); results.setOnCompleteListener(onComplete); if (!results.isComplete()) { mHandler.postDelayed(results::finish, getScrollCaptureRequestTimeout()); @@ -9343,8 +9343,8 @@ public final class ViewRootImpl implements ViewParent, } /** Called by {@link #handleScrollCaptureRequest} when a result is returned */ - private void dispatchScrollCaptureSearchResult( - @NonNull IScrollCaptureCallbacks callbacks, + private void dispatchScrollCaptureSearchResponse( + @NonNull IScrollCaptureResponseListener listener, @NonNull ScrollCaptureSearchResults results) { ScrollCaptureTarget selectedTarget = results.getTopResult(); @@ -9361,7 +9361,7 @@ public final class ViewRootImpl implements ViewParent, if (selectedTarget == null) { response.setDescription("No scrollable targets found in window"); try { - callbacks.onScrollCaptureResponse(response.build()); + listener.onScrollCaptureResponse(response.build()); } catch (RemoteException e) { Log.e(TAG, "Failed to send scroll capture search result", e); } @@ -9387,11 +9387,11 @@ public final class ViewRootImpl implements ViewParent, // Create a connection and return it to the caller ScrollCaptureConnection connection = new ScrollCaptureConnection( - mView.getContext().getMainExecutor(), selectedTarget, callbacks); + mView.getContext().getMainExecutor(), selectedTarget); response.setConnection(connection); try { - callbacks.onScrollCaptureResponse(response.build()); + listener.onScrollCaptureResponse(response.build()); } catch (RemoteException e) { if (DEBUG_SCROLL_CAPTURE) { Log.w(TAG, "Failed to send scroll capture search response.", e); @@ -9691,10 +9691,10 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { + public void requestScrollCapture(IScrollCaptureResponseListener listener) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { - viewAncestor.dispatchScrollCaptureRequest(callbacks); + viewAncestor.dispatchScrollCaptureRequest(listener); } } } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index cf5ec8de0362..c814e5add1b7 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -2619,10 +2619,10 @@ public abstract class Window { /** * System request to begin scroll capture. * - * @param callbacks to receive responses + * @param listener to receive the response * @hide */ - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { + public void requestScrollCapture(IScrollCaptureResponseListener listener) { } /** diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index c203c7903256..967d97be89c2 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -177,7 +177,7 @@ public class EdgeEffect { private long mStartTime; private float mDuration; private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY; - private float mStretchDistanceFraction = 0.1f; + private float mStretchDistanceFraction = 1f; private float mStretchDistance = -1f; private final Interpolator mInterpolator = new DecelerateInterpolator(); @@ -354,13 +354,14 @@ public class EdgeEffect { mDistance = Math.max(0f, mPullDistance); mVelocity = 0; - final float absdd = Math.abs(deltaDistance); - mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA, - mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR)); - if (mPullDistance == 0) { mGlowScaleY = mGlowScaleYStart = 0; + mGlowAlpha = mGlowAlphaStart = 0; } else { + final float absdd = Math.abs(deltaDistance); + mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA, + mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR)); + final float scale = (float) (Math.max(0, 1 - 1 / Math.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3d) / 0.7d); @@ -656,7 +657,7 @@ public class EdgeEffect { // for now leverage placeholder logic if no stretch distance is provided to // consume the displacement ratio times the minimum of the width or height mStretchDistance > 0 ? mStretchDistance : - (mStretchDistanceFraction * Math.min(mWidth, mHeight)) + (mStretchDistanceFraction * Math.max(mWidth, mHeight)) ); } @@ -698,7 +699,9 @@ public class EdgeEffect { mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp; mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp; - mDistance = calculateDistanceFromGlowValues(mGlowScaleY, mGlowAlpha); + if (mState != STATE_PULL) { + mDistance = calculateDistanceFromGlowValues(mGlowScaleY, mGlowAlpha); + } mDisplacement = (mDisplacement + mTargetDisplacement) / 2; if (t >= 1.f - EPSILON) { diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS index 718076b49f77..64570a8ad155 100644 --- a/core/java/android/widget/OWNERS +++ b/core/java/android/widget/OWNERS @@ -5,6 +5,8 @@ alanv@google.com adamp@google.com aurimas@google.com siyamed@google.com +mount@google.com +njawad@google.com per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 21589c9c7d98..0cedcea7b4d4 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -2196,7 +2196,7 @@ public class RemoteViews implements Parcelable, Filter { int recycledViewIndex = findViewIndexToRecycle(target, rvToApply); if (recycledViewIndex >= 0) { View child = target.getChildAt(recycledViewIndex); - if (getViewLayoutId(child) == rvToApply.getLayoutId()) { + if (rvToApply.canRecycleView(child)) { if (nextChild < recycledViewIndex) { target.removeViews(nextChild, recycledViewIndex - nextChild); } @@ -2254,7 +2254,7 @@ public class RemoteViews implements Parcelable, Filter { // application are placed before. ViewTree recycled = target.mChildren.get(recycledViewIndex); // We can only recycle the view if the layout id is the same. - if (getViewLayoutId(recycled.mRoot) == rvToApply.getLayoutId()) { + if (rvToApply.canRecycleView(recycled.mRoot)) { if (recycledViewIndex > nextChild) { target.removeChildren(nextChild, recycledViewIndex - nextChild); } @@ -3726,7 +3726,8 @@ public class RemoteViews implements Parcelable, Filter { * * The {@code stableId} will be used to identify a potential view to recycled when the remote * view is inflated. Views can be re-used if inserted in the same order, potentially with - * some views appearing / disappearing. + * some views appearing / disappearing. To be recycled the view must not change the layout + * used to inflate it or its view id (see {@link RemoteViews#setViewId}). * * Note: if a view is re-used, all the actions will be re-applied on it. However, its properties * are not reset, so what was applied in previous round will have an effect. As a view may be @@ -4426,8 +4427,7 @@ public class RemoteViews implements Parcelable, Filter { /** * Sets an OutlineProvider on the view whose corner radius is a dimension calculated using - * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}. This outline may change shape - * during system transitions. + * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}. * * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0. * Setting margins in pixels will behave poorly when the RemoteViews object is used on a @@ -4440,7 +4440,7 @@ public class RemoteViews implements Parcelable, Filter { /** * Sets an OutlineProvider on the view whose corner radius is a dimension resource with - * {@code resId}. This outline may change shape during system transitions. + * {@code resId}. */ public void setViewOutlinePreferredRadiusDimen(@IdRes int viewId, @DimenRes int resId) { addAction(new SetViewOutlinePreferredRadiusAction(viewId, resId)); @@ -4494,7 +4494,8 @@ public class RemoteViews implements Parcelable, Filter { * Call a method taking one int, a size in pixels, on a view in the layout for this * RemoteViews. * - * The dimension will be resolved from the resources at the time of inflation. + * The dimension will be resolved from the resources at the time the {@link RemoteViews} is + * (re-)applied. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. @@ -4526,7 +4527,8 @@ public class RemoteViews implements Parcelable, Filter { /** * Call a method taking one int, a color, on a view in the layout for this RemoteViews. * - * The ColorStateList will be resolved from the resources at the time of inflation. + * The Color will be resolved from the resources at the time the {@link RemoteViews} is (re-) + * applied. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. @@ -4603,7 +4605,8 @@ public class RemoteViews implements Parcelable, Filter { /** * Call a method taking one ColorStateList on a view in the layout for this RemoteViews. * - * The ColorStateList will be resolved from the resources at the time of inflation. + * The ColorStateList will be resolved from the resources at the time the {@link RemoteViews} is + * (re-)applied. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. @@ -4642,7 +4645,8 @@ public class RemoteViews implements Parcelable, Filter { * Call a method taking one float, a size in pixels, on a view in the layout for this * RemoteViews. * - * The dimension will be resolved from the resources at the time of inflation. + * The dimension will be resolved from the resources at the time the {@link RemoteViews} is + * (re-)applied. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. @@ -4658,7 +4662,8 @@ public class RemoteViews implements Parcelable, Filter { * Call a method taking one float, a size in pixels, on a view in the layout for this * RemoteViews. * - * The dimension will be resolved from the specified dimension at the time of inflation. + * The dimension will be resolved from the resources at the time the {@link RemoteViews} is + * (re-)applied. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. @@ -4720,7 +4725,8 @@ public class RemoteViews implements Parcelable, Filter { /** * Call a method taking one CharSequence on a view in the layout for this RemoteViews. * - * The CharSequence will be resolved from the resources at the time of inflation. + * The CharSequence will be resolved from the resources at the time the {@link RemoteViews} is + * (re-)applied. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. @@ -5111,6 +5117,7 @@ public class RemoteViews implements Parcelable, Filter { View v = inflater.inflate(rv.getLayoutId(), parent, false); if (mViewId != View.NO_ID) { v.setId(mViewId); + v.setTagInternal(R.id.remote_views_override_id, mViewId); } v.setTagInternal(R.id.widget_frame, rv.getLayoutId()); return v; @@ -5330,6 +5337,13 @@ public class RemoteViews implements Parcelable, Filter { reapply(context, v, handler, size, colorResources, true); } + /** @hide */ + public boolean canRecycleView(View v) { + Integer overrideIdTag = (Integer) v.getTag(R.id.remote_views_override_id); + int overrideId = overrideIdTag == null ? View.NO_ID : overrideIdTag; + return (Integer) v.getTag(R.id.widget_frame) == getLayoutId() && mViewId == overrideId; + } + // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls // should set it to false. private void reapply(Context context, View v, InteractionHandler handler, SizeF size, @@ -5342,7 +5356,7 @@ public class RemoteViews implements Parcelable, Filter { // (orientation or size), we throw an exception, since the layouts may be completely // unrelated. if (hasMultipleLayouts()) { - if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) { + if (!rvToApply.canRecycleView(v)) { throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" + " that does not share the same root layout id."); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/IOneHanded.aidl b/core/java/android/window/SizeConfigurationBuckets.aidl index 008b5087d7da..adb57f06da7e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/IOneHanded.aidl +++ b/core/java/android/window/SizeConfigurationBuckets.aidl @@ -14,20 +14,6 @@ * limitations under the License. */ -package com.android.wm.shell.onehanded; +package android.window; -/** - * Interface that is exposed to remote callers to manipulate the OneHanded feature. - */ -interface IOneHanded { - - /** - * Enters one handed mode. - */ - oneway void startOneHanded() = 1; - - /** - * Exits one handed mode. - */ - oneway void stopOneHanded() = 2; -} +parcelable SizeConfigurationBuckets; diff --git a/core/java/android/window/SizeConfigurationBuckets.java b/core/java/android/window/SizeConfigurationBuckets.java new file mode 100644 index 000000000000..7422f2449a8d --- /dev/null +++ b/core/java/android/window/SizeConfigurationBuckets.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; +import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.Configuration; +import android.os.Parcelable; +import android.util.SparseIntArray; + +import com.android.internal.util.DataClass; + +import java.util.Arrays; + +/** + * Contains size-configuration buckets used to prevent excessive configuration changes during + * resize. + * + * These configurations are collected from application's resources based on size-sensitive + * qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800 + * and drawable-sw400dp will be added to both as 400. + * + * @hide + */ +@DataClass(genAidl = true) +public final class SizeConfigurationBuckets implements Parcelable { + + /** Horizontal (screenWidthDp) buckets */ + @Nullable + private final int[] mHorizontal; + + /** Vertical (screenHeightDp) buckets */ + @Nullable + private final int[] mVertical; + + /** Smallest (smallestScreenWidthDp) buckets */ + @Nullable + private final int[] mSmallest; + + public SizeConfigurationBuckets(Configuration[] sizeConfigurations) { + SparseIntArray horizontal = new SparseIntArray(); + SparseIntArray vertical = new SparseIntArray(); + SparseIntArray smallest = new SparseIntArray(); + for (int i = sizeConfigurations.length - 1; i >= 0; i--) { + Configuration config = sizeConfigurations[i]; + if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { + vertical.put(config.screenHeightDp, 0); + } + if (config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) { + horizontal.put(config.screenWidthDp, 0); + } + if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { + smallest.put(config.smallestScreenWidthDp, 0); + } + } + mHorizontal = horizontal.copyKeys(); + mVertical = vertical.copyKeys(); + mSmallest = smallest.copyKeys(); + } + + /** + * Get the changes between two configurations but don't count changes in sizes if they don't + * cross boundaries that are important to the app. + * + * This is a static helper to deal with null `buckets`. When no buckets have been specified, + * this actually filters out all 3 size-configs. This is legacy behavior. + */ + public static int filterDiff(int diff, Configuration oldConfig, Configuration newConfig, + @Nullable SizeConfigurationBuckets buckets) { + if (buckets == null) { + return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE); + } + if ((diff & CONFIG_SCREEN_SIZE) != 0) { + final boolean crosses = buckets.crossesHorizontalSizeThreshold(oldConfig.screenWidthDp, + newConfig.screenWidthDp) + || buckets.crossesVerticalSizeThreshold(oldConfig.screenHeightDp, + newConfig.screenHeightDp); + if (!crosses) { + diff &= ~CONFIG_SCREEN_SIZE; + } + } + if ((diff & CONFIG_SMALLEST_SCREEN_SIZE) != 0) { + final int oldSmallest = oldConfig.smallestScreenWidthDp; + final int newSmallest = newConfig.smallestScreenWidthDp; + if (!buckets.crossesSmallestSizeThreshold(oldSmallest, newSmallest)) { + diff &= ~CONFIG_SMALLEST_SCREEN_SIZE; + } + } + return diff; + } + + private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) { + return crossesSizeThreshold(mHorizontal, firstDp, secondDp); + } + + private boolean crossesVerticalSizeThreshold(int firstDp, int secondDp) { + return crossesSizeThreshold(mVertical, firstDp, secondDp); + } + + private boolean crossesSmallestSizeThreshold(int firstDp, int secondDp) { + return crossesSizeThreshold(mSmallest, firstDp, secondDp); + } + + /** + * The purpose of this method is to decide whether the activity needs to be relaunched upon + * changing its size. In most cases the activities don't need to be relaunched, if the resize + * is small, all the activity content has to do is relayout itself within new bounds. There are + * cases however, where the activity's content would be completely changed in the new size and + * the full relaunch is required. + * + * The activity will report to us vertical and horizontal thresholds after which a relaunch is + * required. These thresholds are collected from the application resource qualifiers. For + * example, if application has layout-w600dp resource directory, then it needs a relaunch when + * we resize from width of 650dp to 550dp, as it crosses the 600dp threshold. However, if + * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side + * of the threshold. + */ + private static boolean crossesSizeThreshold(int[] thresholds, int firstDp, + int secondDp) { + if (thresholds == null) { + return false; + } + for (int i = thresholds.length - 1; i >= 0; i--) { + final int threshold = thresholds[i]; + if ((firstDp < threshold && secondDp >= threshold) + || (firstDp >= threshold && secondDp < threshold)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return Arrays.toString(mHorizontal) + " " + Arrays.toString(mVertical) + " " + + Arrays.toString(mSmallest); + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/window/SizeConfigurationBuckets.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new SizeConfigurationBuckets. + * + * @param horizontal + * Horizontal (screenWidthDp) buckets + * @param vertical + * Vertical (screenHeightDp) buckets + * @param smallest + * Smallest (smallestScreenWidthDp) buckets + */ + @DataClass.Generated.Member + public SizeConfigurationBuckets( + @Nullable int[] horizontal, + @Nullable int[] vertical, + @Nullable int[] smallest) { + this.mHorizontal = horizontal; + this.mVertical = vertical; + this.mSmallest = smallest; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * Horizontal (screenWidthDp) buckets + */ + @DataClass.Generated.Member + public @Nullable int[] getHorizontal() { + return mHorizontal; + } + + /** + * Vertical (screenHeightDp) buckets + */ + @DataClass.Generated.Member + public @Nullable int[] getVertical() { + return mVertical; + } + + /** + * Smallest (smallestScreenWidthDp) buckets + */ + @DataClass.Generated.Member + public @Nullable int[] getSmallest() { + return mSmallest; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mHorizontal != null) flg |= 0x1; + if (mVertical != null) flg |= 0x2; + if (mSmallest != null) flg |= 0x4; + dest.writeByte(flg); + if (mHorizontal != null) dest.writeIntArray(mHorizontal); + if (mVertical != null) dest.writeIntArray(mVertical); + if (mSmallest != null) dest.writeIntArray(mSmallest); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ SizeConfigurationBuckets(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + int[] horizontal = (flg & 0x1) == 0 ? null : in.createIntArray(); + int[] vertical = (flg & 0x2) == 0 ? null : in.createIntArray(); + int[] smallest = (flg & 0x4) == 0 ? null : in.createIntArray(); + + this.mHorizontal = horizontal; + this.mVertical = vertical; + this.mSmallest = smallest; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<SizeConfigurationBuckets> CREATOR + = new Parcelable.Creator<SizeConfigurationBuckets>() { + @Override + public SizeConfigurationBuckets[] newArray(int size) { + return new SizeConfigurationBuckets[size]; + } + + @Override + public SizeConfigurationBuckets createFromParcel(@NonNull android.os.Parcel in) { + return new SizeConfigurationBuckets(in); + } + }; + + @DataClass.Generated( + time = 1615845864280L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/window/SizeConfigurationBuckets.java", + inputSignatures = "private final @android.annotation.Nullable int[] mHorizontal\nprivate final @android.annotation.Nullable int[] mVertical\nprivate final @android.annotation.Nullable int[] mSmallest\npublic static int filterDiff(int,android.content.res.Configuration,android.content.res.Configuration,android.window.SizeConfigurationBuckets)\nprivate boolean crossesHorizontalSizeThreshold(int,int)\nprivate boolean crossesVerticalSizeThreshold(int,int)\nprivate boolean crossesSmallestSizeThreshold(int,int)\nprivate static boolean crossesSizeThreshold(int[],int,int)\npublic @java.lang.Override java.lang.String toString()\nclass SizeConfigurationBuckets extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java index c2e1426bc4fe..7baa53bcd56d 100644 --- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java +++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java @@ -16,6 +16,9 @@ package com.android.internal.accessibility.util; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; @@ -28,6 +31,10 @@ import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY; +import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL; +import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN; +import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE; +import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW; import android.content.ComponentName; import android.view.accessibility.AccessibilityManager; @@ -113,6 +120,19 @@ public final class AccessibilityStatsLogUtils { UNKNOWN_STATUS); } + /** + * Logs the magnification activated mode and its duration of the usage. + * Calls this when the magnification is disabled. + * + * @param mode The activated magnification mode. + * @param duration The duration in milliseconds during the magnification is activated. + */ + public static void logMagnificationUsageState(int mode, long duration) { + FrameworkStatsLog.write(FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED, + convertToLoggingMagnificationMode(mode), + duration); + } + private static int convertToLoggingShortcutType(@ShortcutType int shortcutType) { switch (shortcutType) { case ACCESSIBILITY_BUTTON: @@ -127,4 +147,18 @@ public final class AccessibilityStatsLogUtils { return enabled ? ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__ENABLED : ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED; } + + private static int convertToLoggingMagnificationMode(int mode) { + switch (mode) { + case ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN: + return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN; + case ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW: + return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW; + case ACCESSIBILITY_MAGNIFICATION_MODE_ALL: + return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL; + + default: + return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE; + } + } } diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index ee988781e51d..52801faf9c36 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -474,6 +474,12 @@ public final class SystemUiDeviceConfigFlags { public static final String SHARE_USE_SERVICE_TARGETS = "share_use_service_targets"; + /* + * (long) The duration that the home button must be pressed before triggering Assist + */ + public static final String HOME_BUTTON_LONG_PRESS_DURATION_MS = + "home_button_long_press_duration_ms"; + private SystemUiDeviceConfigFlags() { } } diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index e602cd2c8890..a60b31078a86 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -504,8 +504,10 @@ public abstract class FileSystemProvider extends DocumentsProvider { final File visibleFile = getFileForDocId(documentId, true); final int pfdMode = ParcelFileDescriptor.parseMode(mode); - if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY || visibleFile == null) { - return openFileForRead(file); + if (visibleFile == null) { + return ParcelFileDescriptor.open(file, pfdMode); + } else if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY) { + return openFileForRead(visibleFile); } else { try { // When finished writing, kick off media scanner @@ -522,6 +524,10 @@ public abstract class FileSystemProvider extends DocumentsProvider { private ParcelFileDescriptor openFileForRead(final File target) throws FileNotFoundException { final Uri uri = MediaStore.scanFile(getContext().getContentResolver(), target); + if (uri == null) { + Log.w(TAG, "Failed to retrieve media store URI for: " + target); + return ParcelFileDescriptor.open(target, ParcelFileDescriptor.MODE_READ_ONLY); + } // Passing the calling uid via EXTRA_MEDIA_CAPABILITIES_UID, so that the decision to // transcode or not transcode can be made based upon the calling app's uid, and not based @@ -532,7 +538,8 @@ public abstract class FileSystemProvider extends DocumentsProvider { final AssetFileDescriptor afd = getContext().getContentResolver().openTypedAssetFileDescriptor(uri, "*/*", opts); if (afd == null) { - return null; + Log.w(TAG, "Failed to open with media_capabilities uid for URI: " + uri); + return ParcelFileDescriptor.open(target, ParcelFileDescriptor.MODE_READ_ONLY); } return afd.getParcelFileDescriptor(); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 11466f4bc042..33b55ac2f0a0 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -169,7 +169,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 194; + static final int VERSION = 195; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -1009,8 +1009,12 @@ public class BatteryStatsImpl extends BatteryStats { protected @Nullable MeasuredEnergyStats mGlobalMeasuredEnergyStats; /** Last known screen state. Needed for apportioning display energy. */ int mScreenStateAtLastEnergyMeasurement = Display.STATE_UNKNOWN; + /** Bluetooth Power calculator for attributing measured bluetooth charge consumption to uids */ + @Nullable BluetoothPowerCalculator mBluetoothPowerCalculator = null; /** Cpu Power calculator for attributing measured cpu charge consumption to uids */ @Nullable CpuPowerCalculator mCpuPowerCalculator = null; + /** Wifi Power calculator for attributing measured wifi charge consumption to uids */ + @Nullable WifiPowerCalculator mWifiPowerCalculator = null; /** * These provide time bases that discount the time the device is plugged @@ -6967,6 +6971,16 @@ public class BatteryStatsImpl extends BatteryStats { } @Override + public long getBluetoothMeasuredBatteryConsumptionUC() { + return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH); + } + + @Override + public long getCpuMeasuredBatteryConsumptionUC() { + return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU); + } + + @Override public long getScreenOnMeasuredBatteryConsumptionUC() { return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON); } @@ -6977,8 +6991,8 @@ public class BatteryStatsImpl extends BatteryStats { } @Override - public long getCpuMeasuredBatteryConsumptionUC() { - return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU); + public long getWifiMeasuredBatteryConsumptionUC() { + return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI); } /** @@ -7815,6 +7829,26 @@ public class BatteryStatsImpl extends BatteryStats { return mUidMeasuredEnergyStats.getAccumulatedCustomBucketCharges(); } + @Override + public long getBluetoothMeasuredBatteryConsumptionUC() { + return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH); + } + + @Override + public long getCpuMeasuredBatteryConsumptionUC() { + return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU); + } + + @Override + public long getScreenOnMeasuredBatteryConsumptionUC() { + return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON); + } + + @Override + public long getWifiMeasuredBatteryConsumptionUC() { + return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI); + } + /** * Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time * since last marked. Also sets the mark time for both these timers. @@ -8482,16 +8516,6 @@ public class BatteryStatsImpl extends BatteryStats { } } - @Override - public long getScreenOnMeasuredBatteryConsumptionUC() { - return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON); - } - - @Override - public long getCpuMeasuredBatteryConsumptionUC() { - return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU); - } - void initNetworkActivityLocked() { detachIfNotNull(mNetworkByteActivityCounters); mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES]; @@ -11437,7 +11461,7 @@ public class BatteryStatsImpl extends BatteryStats { * @param info The energy information from the WiFi controller. */ public void updateWifiState(@Nullable final WifiActivityEnergyInfo info, - long elapsedRealtimeMs, long uptimeMs) { + final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) { if (DEBUG_ENERGY) { Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces)); } @@ -11459,9 +11483,21 @@ public class BatteryStatsImpl extends BatteryStats { if (delta != null) { mNetworkStatsPool.release(delta); } + if (mIgnoreNextExternalStats) { + // TODO: Strictly speaking, we should re-mark all 5 timers for each uid (and the + // global one) here like we do for display. But I'm not sure it's worth the + // complicated code for a codepath that shouldn't ever actually happen in real + // life. + } return; } + final ArrayMap<Uid, Double> uidEstimatedConsumptionMah = + (mGlobalMeasuredEnergyStats != null + && mWifiPowerCalculator != null && consumedChargeUC > 0) ? + new ArrayMap<>() : null; + double totalEstimatedConsumptionMah = 0; + SparseLongArray rxPackets = new SparseLongArray(); SparseLongArray txPackets = new SparseLongArray(); long totalTxPackets = 0; @@ -11496,6 +11532,7 @@ public class BatteryStatsImpl extends BatteryStats { mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( entry.rxPackets); + // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum? rxPackets.put(u.getUid(), entry.rxPackets); // Sum the total number of packets so that the Rx Power can @@ -11515,12 +11552,42 @@ public class BatteryStatsImpl extends BatteryStats { mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( entry.txPackets); + // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum? txPackets.put(u.getUid(), entry.txPackets); // Sum the total number of packets so that the Tx Power can // be evenly distributed amongst the apps. totalTxPackets += entry.txPackets; } + + // Calculate consumed energy for this uid. Only do so if WifiReporting isn't + // enabled (if it is, we'll do it later instead using info). + if (uidEstimatedConsumptionMah != null && info == null && !mHasWifiReporting) { + final long uidRunningMs = u.mWifiRunningTimer + .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000; + if (uidRunningMs > 0) u.mWifiRunningTimer.setMark(elapsedRealtimeMs); + + final long uidScanMs = u.mWifiScanTimer + .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000; + if (uidScanMs > 0) u.mWifiScanTimer.setMark(elapsedRealtimeMs); + + long uidBatchScanMs = 0; + for (int bn = 0; bn < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bn++) { + if (u.mWifiBatchedScanTimer[bn] != null) { + long bnMs = u.mWifiBatchedScanTimer[bn] + .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000; + if (bnMs > 0) { + u.mWifiBatchedScanTimer[bn].setMark(elapsedRealtimeMs); + } + uidBatchScanMs += bnMs; + } + } + + addDoubleToUidMap(uidEstimatedConsumptionMah, u, + mWifiPowerCalculator.calcPowerWithoutControllerDataMah( + entry.rxPackets, entry.txPackets, + uidRunningMs, uidScanMs, uidBatchScanMs)); + } } mNetworkStatsPool.release(delta); delta = null; @@ -11581,15 +11648,14 @@ public class BatteryStatsImpl extends BatteryStats { for (int i = 0; i < uidStatsSize; i++) { final Uid uid = mUidStats.valueAt(i); - long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked( + final long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; + long scanRxTimeSinceMarkMs = scanTimeSinceMarkMs; // not final + long scanTxTimeSinceMarkMs = scanTimeSinceMarkMs; // not final if (scanTimeSinceMarkMs > 0) { // Set the new mark so that next time we get new data since this point. uid.mWifiScanTimer.setMark(elapsedRealtimeMs); - long scanRxTimeSinceMarkMs = scanTimeSinceMarkMs; - long scanTxTimeSinceMarkMs = scanTimeSinceMarkMs; - // Our total scan time is more than the reported Tx/Rx time. // This is possible because the cost of a scan is approximate. // Let's normalize the result so that we evenly blame each app @@ -11623,6 +11689,7 @@ public class BatteryStatsImpl extends BatteryStats { // Distribute evenly the power consumed while Idle to each app holding a WiFi // lock. + long myIdleTimeMs = 0; final long wifiLockTimeSinceMarkMs = uid.mFullWifiLockTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; @@ -11630,8 +11697,7 @@ public class BatteryStatsImpl extends BatteryStats { // Set the new mark so that next time we get new data since this point. uid.mFullWifiLockTimer.setMark(elapsedRealtimeMs); - final long myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs) - / totalWifiLockTimeMs; + myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs) / totalWifiLockTimeMs; if (DEBUG_ENERGY) { Slog.d(TAG, " IdleTime for UID " + uid.getUid() + ": " + myIdleTimeMs + " ms"); @@ -11639,6 +11705,12 @@ public class BatteryStatsImpl extends BatteryStats { uid.getOrCreateWifiControllerActivityLocked().getIdleTimeCounter() .addCountLocked(myIdleTimeMs); } + + if (uidEstimatedConsumptionMah != null) { + double uidEstMah = mWifiPowerCalculator.calcPowerFromControllerDataMah( + scanRxTimeSinceMarkMs, scanTxTimeSinceMarkMs, myIdleTimeMs); + addDoubleToUidMap(uidEstimatedConsumptionMah, uid, uidEstMah); + } } if (DEBUG_ENERGY) { @@ -11658,6 +11730,11 @@ public class BatteryStatsImpl extends BatteryStats { } uid.getOrCreateWifiControllerActivityLocked().getTxTimeCounters()[0] .addCountLocked(myTxTimeMs); + if (uidEstimatedConsumptionMah != null) { + addDoubleToUidMap(uidEstimatedConsumptionMah, uid, + mWifiPowerCalculator.calcPowerFromControllerDataMah( + 0, myTxTimeMs, 0)); + } } // Distribute the remaining Rx power appropriately between all apps that received @@ -11672,6 +11749,11 @@ public class BatteryStatsImpl extends BatteryStats { } uid.getOrCreateWifiControllerActivityLocked().getRxTimeCounter() .addCountLocked(myRxTimeMs); + if (uidEstimatedConsumptionMah != null) { + addDoubleToUidMap(uidEstimatedConsumptionMah, uid, + mWifiPowerCalculator.calcPowerFromControllerDataMah( + myRxTimeMs, 0, 0)); + } } // Any left over power use will be picked up by the WiFi category in BatteryStatsHelper. @@ -11690,10 +11772,11 @@ public class BatteryStatsImpl extends BatteryStats { // POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. final double opVolt = mPowerProfile.getAveragePower( PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; + double controllerMaMs = 0; if (opVolt != 0) { // We store the power drain as mAms. - mWifiActivity.getPowerCounter().addCountLocked( - (long) (info.getControllerEnergyUsedMicroJoules() / opVolt)); + controllerMaMs = info.getControllerEnergyUsedMicroJoules() / opVolt; + mWifiActivity.getPowerCounter().addCountLocked((long) controllerMaMs); } // Converting uWs to mAms. // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms @@ -11705,6 +11788,29 @@ public class BatteryStatsImpl extends BatteryStats { (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR); addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); mTmpRailStats.resetWifiTotalEnergyUsed(); + + if (uidEstimatedConsumptionMah != null) { + totalEstimatedConsumptionMah = Math.max(controllerMaMs / MILLISECONDS_IN_HOUR, + mWifiPowerCalculator.calcPowerFromControllerDataMah( + rxTimeMs, txTimeMs, idleTimeMs)); + } + } + + // Update the MeasuredEnergyStats information. + if (uidEstimatedConsumptionMah != null) { + mGlobalMeasuredEnergyStats.updateStandardBucket( + MeasuredEnergyStats.POWER_BUCKET_WIFI, consumedChargeUC); + + // Now calculate the consumption for each uid, according to its proportional usage. + if (!mHasWifiReporting) { + final long globalTimeMs = mGlobalWifiRunningTimer + .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000; + mGlobalWifiRunningTimer.setMark(elapsedRealtimeMs); + totalEstimatedConsumptionMah = mWifiPowerCalculator + .calcGlobalPowerWithoutControllerDataMah(globalTimeMs); + } + distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_WIFI, + consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah); } } } @@ -11948,7 +12054,7 @@ public class BatteryStatsImpl extends BatteryStats { * @param info The energy information from the bluetooth controller. */ public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info, - long elapsedRealtimeMs, long uptimeMs) { + final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) { if (DEBUG_ENERGY) { Slog.d(TAG, "Updating bluetooth stats: " + info); } @@ -11980,6 +12086,11 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms"); } + final ArrayMap<Uid, Double> uidEstimatedConsumptionMah = + (mGlobalMeasuredEnergyStats != null + && mBluetoothPowerCalculator != null && consumedChargeUC > 0) ? + new ArrayMap<>() : null; + long totalScanTimeMs = 0; final int uidCount = mUidStats.size(); @@ -12038,6 +12149,12 @@ public class BatteryStatsImpl extends BatteryStats { counter.getRxTimeCounter().addCountLocked(scanTimeRxSinceMarkMs); counter.getTxTimeCounters()[0].addCountLocked(scanTimeTxSinceMarkMs); + if (uidEstimatedConsumptionMah != null) { + addDoubleToUidMap(uidEstimatedConsumptionMah, u, + mBluetoothPowerCalculator.calculatePowerMah( + scanTimeRxSinceMarkMs, scanTimeTxSinceMarkMs, 0)); + } + leftOverRxTimeMs -= scanTimeRxSinceMarkMs; leftOverTxTimeMs -= scanTimeTxSinceMarkMs; } @@ -12098,6 +12215,11 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs); } counter.getRxTimeCounter().addCountLocked(timeRxMs); + + if (uidEstimatedConsumptionMah != null) { + addDoubleToUidMap(uidEstimatedConsumptionMah, u, + mBluetoothPowerCalculator.calculatePowerMah(timeRxMs, 0, 0)); + } } if (totalTxBytes > 0 && txBytes > 0) { @@ -12106,6 +12228,11 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs); } counter.getTxTimeCounters()[0].addCountLocked(timeTxMs); + + if (uidEstimatedConsumptionMah != null) { + addDoubleToUidMap(uidEstimatedConsumptionMah, u, + mBluetoothPowerCalculator.calculatePowerMah(0, timeTxMs, 0)); + } } } } @@ -12117,12 +12244,26 @@ public class BatteryStatsImpl extends BatteryStats { // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. final double opVolt = mPowerProfile.getAveragePower( PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; + double controllerMaMs = 0; if (opVolt != 0) { + controllerMaMs = (info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy) + / opVolt; // We store the power drain as mAms. - mBluetoothActivity.getPowerCounter().addCountLocked( - (long) ((info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy) - / opVolt)); + mBluetoothActivity.getPowerCounter().addCountLocked((long) controllerMaMs); + } + + // Update the MeasuredEnergyStats information. + if (uidEstimatedConsumptionMah != null) { + mGlobalMeasuredEnergyStats.updateStandardBucket( + MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH, consumedChargeUC); + + double totalEstimatedMah + = mBluetoothPowerCalculator.calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs); + totalEstimatedMah = Math.max(totalEstimatedMah, controllerMaMs / MILLISECONDS_IN_HOUR); + distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH, + consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah); } + mLastBluetoothActivityInfo.set(info); } @@ -12311,37 +12452,17 @@ public class BatteryStatsImpl extends BatteryStats { // If multidisplay becomes a reality, this is probably more reasonable than pooling. // On the first pass, collect total time since mark so that we can normalize power. - long totalFgTimeMs = 0L; - final ArrayMap<Uid, Long> fgTimeMsArray = new ArrayMap<>(); + final ArrayMap<Uid, Double> fgTimeUsArray = new ArrayMap<>(); final long elapsedRealtimeUs = elapsedRealtimeMs * 1000; // TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids) final int uidStatsSize = mUidStats.size(); for (int i = 0; i < uidStatsSize; i++) { final Uid uid = mUidStats.valueAt(i); - final long fgTimeMs = uid.markProcessForegroundTimeUs(elapsedRealtimeMs, true) / 1000; - if (fgTimeMs == 0) continue; - fgTimeMsArray.put(uid, fgTimeMs); - totalFgTimeMs += fgTimeMs; - } - long totalDisplayChargeMC = chargeUC / 1000; // not final - - // Actually assign and distribute power usage to apps based on their fg time since mark. - // TODO(b/175726326): Decide on 'energy' units and make sure algorithm won't overflow. - final long fgTimeArraySize = fgTimeMsArray.size(); - for (int i = 0; i < fgTimeArraySize; i++) { - final Uid uid = fgTimeMsArray.keyAt(i); - final long fgTimeMs = fgTimeMsArray.valueAt(i); - - // Using long division: "appEnergy = totalEnergy * appFg/totalFg + 0.5" with rounding - final long appDisplayChargeMC = - (totalDisplayChargeMC * fgTimeMs + (totalFgTimeMs / 2)) - / totalFgTimeMs; - uid.addChargeToStandardBucketLocked(appDisplayChargeMC * 1000, powerBucket); - - // To mitigate round-off errors, remove this app from numerator & denominator totals - totalDisplayChargeMC -= appDisplayChargeMC; - totalFgTimeMs -= fgTimeMs; + final long fgTimeUs = uid.markProcessForegroundTimeUs(elapsedRealtimeMs, true); + if (fgTimeUs == 0) continue; + fgTimeUsArray.put(uid, (double) fgTimeUs); } + distributeEnergyToUidsLocked(powerBucket, chargeUC, fgTimeUsArray, 0); } /** @@ -12389,6 +12510,54 @@ public class BatteryStatsImpl extends BatteryStats { } /** + * Attributes energy (for the given bucket) to each uid according to the following formula: + * blamedEnergy[uid] = totalEnergy * ratioNumerators[uid] / ratioDenominator; + * <p>Does nothing if ratioDenominator is 0. + * + * <p>Here, ratioDenominator = max(sumOfAllRatioNumerators, minRatioDenominator), + * so if given minRatioDenominator <= 0, then sumOfAllRatioNumerators will be used implicitly. + * + * <p>Note that ratioNumerators and minRatioDenominator must use the same units, but need not + * use the same units as totalConsumedChargeUC (which must be in microcoulombs). + * + * <p>A consequence of minRatioDenominator is that the sum over all uids might be less than + * totalConsumedChargeUC. This is intentional; the remainder is purposefully unnaccounted rather + * than incorrectly blamed on uids, and implies unknown (non-uid) sources of drain. + */ + // TODO(b/182845832): Use some sort of "SparseDoubleArray" instead of ArrayMap<Uid, Double>. + private void distributeEnergyToUidsLocked(@StandardPowerBucket int bucket, + long totalConsumedChargeUC, ArrayMap<Uid, Double> ratioNumerators, + double minRatioDenominator) { + + // If the sum of all app usage was greater than the total, use that instead: + double sumRatioNumerators = 0; + for (int i = ratioNumerators.size() - 1; i >= 0; i--) { + sumRatioNumerators += ratioNumerators.valueAt(i); + } + final double ratioDenominator = Math.max(sumRatioNumerators, minRatioDenominator); + if (ratioDenominator <= 0) return; + + for (int i = ratioNumerators.size() - 1; i >= 0; i--) { + final Uid uid = ratioNumerators.keyAt(i); + final double ratioNumerator = ratioNumerators.valueAt(i); + final long uidActualUC + = (long) (totalConsumedChargeUC * ratioNumerator / ratioDenominator + 0.5); + uid.addChargeToStandardBucketLocked(uidActualUC, bucket); + } + } + + /** Adds the summand to the value stored in uidMap for the given uid. */ + // TODO(b/182845832): Use some sort of "SparseDoubleArray" instead of ArrayMap<Uid, Double>. + private static void addDoubleToUidMap(ArrayMap<Uid, Double> uidMap, Uid uid, double summand) { + if (uidMap == null) return; + final Double oldVal = uidMap.get(uid); + if (oldVal != null) { + summand += oldVal; + } + uidMap.put(uid, summand); + } + + /** * Read and record Rail Energy data. */ public void updateRailStatsLocked() { @@ -14222,15 +14391,20 @@ public class BatteryStatsImpl extends BatteryStats { if (mGlobalMeasuredEnergyStats == null) { mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - return; } else { supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo( supportedStandardBuckets, numCustomBuckets); } + if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH]) { + mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile); + } if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU]) { mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile); } + if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_WIFI]) { + mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile); + } } if (supportedBucketMismatch) { diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java index 7d42de4486a4..db1403479f8a 100644 --- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java +++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java @@ -15,8 +15,11 @@ */ package com.android.internal.os; +import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; + import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryStats.ControllerActivityCounter; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.Process; @@ -65,17 +68,19 @@ public class BluetoothPowerCalculator extends PowerCalculator { builder.getUidBatteryConsumerBuilders(); for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); - calculateApp(app, total); + calculateApp(app, total, query); if (app.getUid() == Process.BLUETOOTH_UID) { app.excludeFromBatteryUsageStats(); systemBatteryConsumerBuilder.addUidBatteryConsumer(app); } } - final BatteryStats.ControllerActivityCounter activityCounter = + final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ? + POWER_DATA_UNAVAILABLE : batteryStats.getBluetoothMeasuredBatteryConsumptionUC(); + final ControllerActivityCounter activityCounter = batteryStats.getBluetoothControllerActivity(); final long systemDurationMs = calculateDuration(activityCounter); - final double systemPowerMah = calculatePower(activityCounter); + final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter); // Subtract what the apps used, but clamp to 0. final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs); @@ -91,11 +96,16 @@ public class BluetoothPowerCalculator extends PowerCalculator { systemComponentPowerMah); } - private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total) { - final BatteryStats.ControllerActivityCounter activityCounter = + private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total, + BatteryUsageStatsQuery query) { + + final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ? + POWER_DATA_UNAVAILABLE : + app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC(); + final ControllerActivityCounter activityCounter = app.getBatteryStatsUid().getBluetoothControllerActivity(); final long durationMs = calculateDuration(activityCounter); - final double powerMah = calculatePower(activityCounter); + final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter); app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah); @@ -121,10 +131,11 @@ public class BluetoothPowerCalculator extends PowerCalculator { } BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0); - final BatteryStats.ControllerActivityCounter activityCounter = + final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC(); + final ControllerActivityCounter activityCounter = batteryStats.getBluetoothControllerActivity(); - final double systemPowerMah = calculatePower(activityCounter); final long systemDurationMs = calculateDuration(activityCounter); + final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter); // Subtract what the apps used, but clamp to 0. final double powerMah = Math.max(0, systemPowerMah - total.powerMah); @@ -152,10 +163,11 @@ public class BluetoothPowerCalculator extends PowerCalculator { private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, PowerAndDuration total) { - final BatteryStats.ControllerActivityCounter activityCounter = - u.getBluetoothControllerActivity(); + + final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC(); + final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity(); final long durationMs = calculateDuration(activityCounter); - final double powerMah = calculatePower(activityCounter); + final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter); app.bluetoothRunningTimeMs = durationMs; app.bluetoothPowerMah = powerMah; @@ -166,7 +178,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { total.powerMah += powerMah; } - private long calculateDuration(BatteryStats.ControllerActivityCounter counter) { + private long calculateDuration(ControllerActivityCounter counter) { if (counter == null) { return 0; } @@ -176,7 +188,11 @@ public class BluetoothPowerCalculator extends PowerCalculator { + counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); } - private double calculatePower(BatteryStats.ControllerActivityCounter counter) { + /** Returns bluetooth power usage based on the best data available. */ + private double calculatePowerMah(long measuredChargeUC, ControllerActivityCounter counter) { + if (measuredChargeUC != POWER_DATA_UNAVAILABLE) { + return uCtoMah(measuredChargeUC); + } if (counter == null) { return 0; } @@ -195,6 +211,11 @@ public class BluetoothPowerCalculator extends PowerCalculator { counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED); final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); + return calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs); + } + + /** Returns estimated bluetooth power usage based on usage times. */ + public double calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs) { return ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) / (1000 * 60 * 60); } diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING index 791e9ad5ef9d..14cdb0890b25 100644 --- a/core/java/com/android/internal/os/TEST_MAPPING +++ b/core/java/com/android/internal/os/TEST_MAPPING @@ -1,7 +1,11 @@ { "presubmit": [ { - "file_patterns": ["Battery[^/]*\\.java"], + "file_patterns": [ + "Battery[^/]*\\.java", + "Kernel[^/]*\\.java", + "[^/]*Power[^/]*\\.java" + ], "name": "FrameworksCoreTests", "options": [ { "include-filter": "com.android.internal.os.BatteryStatsTests" }, diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java index 98f613fc1c40..b6bfde709cb8 100644 --- a/core/java/com/android/internal/os/WifiPowerCalculator.java +++ b/core/java/com/android/internal/os/WifiPowerCalculator.java @@ -15,6 +15,8 @@ */ package com.android.internal.os; +import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; + import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.BatteryUsageStats; @@ -79,11 +81,6 @@ public class WifiPowerCalculator extends PowerCalculator { public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, - // so always check this field. - final boolean hasWifiPowerReporting = - mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); - final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = builder.getOrCreateSystemBatteryConsumerBuilder( SystemBatteryConsumer.DRAIN_TYPE_WIFI); @@ -97,7 +94,8 @@ public class WifiPowerCalculator extends PowerCalculator { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, - hasWifiPowerReporting); + batteryStats.hasWifiActivityReporting(), + query.shouldForceUsePowerProfileModel()); totalAppDurationMs += powerDurationAndTraffic.durationMs; totalAppPowerMah += powerDurationAndTraffic.powerMah; @@ -115,7 +113,9 @@ public class WifiPowerCalculator extends PowerCalculator { calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, - hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah); + batteryStats.hasWifiActivityReporting(), + query.shouldForceUsePowerProfileModel(), + totalAppDurationMs, totalAppPowerMah); systemBatteryConsumerBuilder .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, @@ -135,11 +135,6 @@ public class WifiPowerCalculator extends PowerCalculator { public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, - // so always check this field. - final boolean hasWifiPowerReporting = - mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); - final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); long totalAppDurationMs = 0; @@ -149,7 +144,7 @@ public class WifiPowerCalculator extends PowerCalculator { final BatterySipper app = sippers.get(i); if (app.drainType == BatterySipper.DrainType.APP) { calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType, - hasWifiPowerReporting); + batteryStats.hasWifiActivityReporting(), /* force use power model*/ false); totalAppDurationMs += powerDurationAndTraffic.durationMs; totalAppPowerMah += powerDurationAndTraffic.powerMah; @@ -169,7 +164,8 @@ public class WifiPowerCalculator extends PowerCalculator { } calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType, - hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah); + batteryStats.hasWifiActivityReporting(), /* force use power model*/ false, + totalAppDurationMs, totalAppPowerMah); bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs; bs.wifiPowerMah += powerDurationAndTraffic.powerMah; @@ -180,8 +176,9 @@ public class WifiPowerCalculator extends PowerCalculator { } private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u, - long rawRealtimeUs, - int statsType, boolean hasWifiPowerReporting) { + long rawRealtimeUs, int statsType, + boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel) { + powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets( BatteryStats.NETWORK_WIFI_RX_DATA, statsType); @@ -195,7 +192,14 @@ public class WifiPowerCalculator extends PowerCalculator { BatteryStats.NETWORK_WIFI_TX_DATA, statsType); - if (hasWifiPowerReporting) { + final long measuredChargeUC = u.getWifiMeasuredBatteryConsumptionUC(); + final boolean isMeasuredPowerAvailable + = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE; + if (isMeasuredPowerAvailable) { + powerDurationAndTraffic.powerMah = uCtoMah(measuredChargeUC); + } + + if (hasWifiActivityReporting && mHasWifiPowerController) { final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity(); if (counter != null) { final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType); @@ -203,9 +207,10 @@ public class WifiPowerCalculator extends PowerCalculator { final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType); powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime; - powerDurationAndTraffic.powerMah = mIdlePowerEstimator.calculatePower(idleTime) - + mTxPowerEstimator.calculatePower(txTime) - + mRxPowerEstimator.calculatePower(rxTime); + if (!isMeasuredPowerAvailable) { + powerDurationAndTraffic.powerMah + = calcPowerFromControllerDataMah(rxTime, txTime, idleTime); + } if (DEBUG && powerDurationAndTraffic.powerMah != 0) { Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime @@ -214,21 +219,20 @@ public class WifiPowerCalculator extends PowerCalculator { } } } else { - final double wifiPacketPower = ( - powerDurationAndTraffic.wifiRxPackets + powerDurationAndTraffic.wifiTxPackets) - * mWifiPowerPerPacket; final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000; - final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000; - long batchScanTimeMs = 0; - for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { - batchScanTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000; - } - powerDurationAndTraffic.durationMs = wifiRunningTime; - powerDurationAndTraffic.powerMah = wifiPacketPower - + mPowerOnPowerEstimator.calculatePower(wifiRunningTime) - + mScanPowerEstimator.calculatePower(wifiScanTimeMs) - + mBatchScanPowerEstimator.calculatePower(batchScanTimeMs); + + if (!isMeasuredPowerAvailable) { + final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000; + long batchTimeMs = 0; + for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { + batchTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000; + } + powerDurationAndTraffic.powerMah = calcPowerWithoutControllerDataMah( + powerDurationAndTraffic.wifiRxPackets, + powerDurationAndTraffic.wifiTxPackets, + wifiRunningTime, wifiScanTimeMs, batchTimeMs); + } if (DEBUG && powerDurationAndTraffic.powerMah != 0) { Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge( @@ -238,12 +242,20 @@ public class WifiPowerCalculator extends PowerCalculator { } private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic, - BatteryStats stats, long rawRealtimeUs, - int statsType, boolean hasWifiPowerReporting, long totalAppDurationMs, - double totalAppPowerMah) { + BatteryStats stats, long rawRealtimeUs, int statsType, + boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel, + long totalAppDurationMs, double totalAppPowerMah) { + long totalDurationMs; - double totalPowerMah; - if (hasWifiPowerReporting) { + double totalPowerMah = 0; + + final long measuredChargeUC = stats.getWifiMeasuredBatteryConsumptionUC(); + final boolean isMeasuredPowerAvailable + = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE; + if (isMeasuredPowerAvailable) { + totalPowerMah = uCtoMah(measuredChargeUC); + } + if (hasWifiActivityReporting && mHasWifiPowerController) { final BatteryStats.ControllerActivityCounter counter = stats.getWifiControllerActivity(); @@ -253,17 +265,19 @@ public class WifiPowerCalculator extends PowerCalculator { totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs; - totalPowerMah = - counter.getPowerCounter().getCountLocked(statsType) / (double) (1000 * 60 * 60); - if (totalPowerMah == 0) { - // Some controllers do not report power drain, so we can calculate it here. - totalPowerMah = mIdlePowerEstimator.calculatePower(idleTimeMs) - + mTxPowerEstimator.calculatePower(txTimeMs) - + mRxPowerEstimator.calculatePower(rxTimeMs); + if (!isMeasuredPowerAvailable) { + totalPowerMah = counter.getPowerCounter().getCountLocked(statsType) + / (double) (1000 * 60 * 60); + if (totalPowerMah == 0) { + // Some controllers do not report power drain, so we can calculate it here. + totalPowerMah = calcPowerFromControllerDataMah(rxTimeMs, txTimeMs, idleTimeMs); + } } } else { totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000; - totalPowerMah = mPowerOnPowerEstimator.calculatePower(totalDurationMs); + if (!isMeasuredPowerAvailable) { + totalPowerMah = calcGlobalPowerWithoutControllerDataMah(totalDurationMs); + } } powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs); @@ -274,6 +288,29 @@ public class WifiPowerCalculator extends PowerCalculator { } } + /** Returns (global or uid) estimated wifi power used using WifiControllerActivity data. */ + public double calcPowerFromControllerDataMah(long rxTimeMs, long txTimeMs, long idleTimeMs) { + return mRxPowerEstimator.calculatePower(rxTimeMs) + + mTxPowerEstimator.calculatePower(txTimeMs) + + mIdlePowerEstimator.calculatePower(idleTimeMs); + } + + /** Returns per-uid estimated wifi power used using non-WifiControllerActivity data. */ + public double calcPowerWithoutControllerDataMah(long rxPackets, long txPackets, + long wifiRunningTimeMs, long wifiScanTimeMs, long wifiBatchScanTimeMs) { + return + (rxPackets + txPackets) * mWifiPowerPerPacket + + mPowerOnPowerEstimator.calculatePower(wifiRunningTimeMs) + + mScanPowerEstimator.calculatePower(wifiScanTimeMs) + + mBatchScanPowerEstimator.calculatePower(wifiBatchScanTimeMs); + + } + + /** Returns global estimated wifi power used using non-WifiControllerActivity data. */ + public double calcGlobalPowerWithoutControllerDataMah(long globalWifiRunningTimeMs) { + return mPowerOnPowerEstimator.calculatePower(globalWifiRunningTimeMs); + } + /** * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB. */ diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 6049486b380c..39bde742e828 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -78,7 +78,7 @@ import android.view.ContextThemeWrapper; import android.view.CrossWindowBlurListeners; import android.view.Gravity; import android.view.IRotationWatcher.Stub; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindowManager; import android.view.InputDevice; import android.view.InputEvent; @@ -3940,12 +3940,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /** * System request to begin scroll capture. * - * @param callbacks to receive responses + * @param listener to receive the response * @hide */ @Override - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { - getViewRootImpl().dispatchScrollCaptureRequest(callbacks); + public void requestScrollCapture(IScrollCaptureResponseListener listener) { + getViewRootImpl().dispatchScrollCaptureRequest(listener); } /** diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index e3d5464ca413..845b3e501c08 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -52,7 +52,9 @@ public class MeasuredEnergyStats { public static final int POWER_BUCKET_SCREEN_DOZE = 1; public static final int POWER_BUCKET_SCREEN_OTHER = 2; public static final int POWER_BUCKET_CPU = 3; - public static final int NUMBER_STANDARD_POWER_BUCKETS = 4; // Buckets above this are custom. + public static final int POWER_BUCKET_WIFI = 4; + public static final int POWER_BUCKET_BLUETOOTH = 5; + public static final int NUMBER_STANDARD_POWER_BUCKETS = 6; // Buckets above this are custom. @IntDef(prefix = {"POWER_BUCKET_"}, value = { POWER_BUCKET_UNKNOWN, @@ -60,6 +62,8 @@ public class MeasuredEnergyStats { POWER_BUCKET_SCREEN_DOZE, POWER_BUCKET_SCREEN_OTHER, POWER_BUCKET_CPU, + POWER_BUCKET_WIFI, + POWER_BUCKET_BLUETOOTH, }) @Retention(RetentionPolicy.SOURCE) public @interface StandardPowerBucket { diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index b57b4b959334..f3d085814baa 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -21,6 +21,7 @@ import android.telephony.CallAttributes; import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.DataConnectionRealTimeInfo; +import android.telephony.LinkCapacityEstimate; import android.telephony.TelephonyDisplayInfo; import android.telephony.PhoneCapability; import android.telephony.PhysicalChannelConfig; @@ -73,4 +74,5 @@ oneway interface IPhoneStateListener { void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs); void onDataEnabledChanged(boolean enabled, int reason); void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType); + void onLinkCapacityEstimateChanged(in List<LinkCapacityEstimate> linkCapacityEstimateList); } diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 83691ee64103..34187687c4e8 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -23,6 +23,7 @@ import android.telephony.BarringInfo; import android.telephony.CallQuality; import android.telephony.CellIdentity; import android.telephony.CellInfo; +import android.telephony.LinkCapacityEstimate; import android.telephony.TelephonyDisplayInfo; import android.telephony.ims.ImsReasonInfo; import android.telephony.PhoneCapability; @@ -96,4 +97,6 @@ interface ITelephonyRegistry { in List<PhysicalChannelConfig> configs); void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason); void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType); + void notifyLinkCapacityEstimateChanged(in int phoneId, in int subId, + in List<LinkCapacityEstimate> linkCapacityEstimateList); } diff --git a/core/java/com/android/internal/util/TrafficStatsConstants.java b/core/java/com/android/internal/util/TrafficStatsConstants.java index 413be484dc32..131114cad460 100644 --- a/core/java/com/android/internal/util/TrafficStatsConstants.java +++ b/core/java/com/android/internal/util/TrafficStatsConstants.java @@ -21,24 +21,8 @@ package com.android.internal.util; * @hide */ public class TrafficStatsConstants { - // These tags are used by the network stack to do traffic for its own purposes. Traffic - // tagged with these will be counted toward the network stack and must stay inside the - // range defined by - // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_START} and - // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_END}. - public static final int TAG_SYSTEM_DHCP = 0xFFFFFE01; - public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFE02; - public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFE03; public static final int TAG_SYSTEM_NTP = 0xFFFFFF41; public static final int TAG_SYSTEM_GPS = 0xFFFFFF44; public static final int TAG_SYSTEM_PAC = 0xFFFFFF45; - - // These tags are used by the network stack to do traffic on behalf of apps. Traffic - // tagged with these will be counted toward the app on behalf of which the network - // stack is doing this traffic. These values must stay inside the range defined by - // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_START} and - // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_END}. - public static final int TAG_SYSTEM_PROBE = 0xFFFFFF81; - public static final int TAG_SYSTEM_DNS = 0xFFFFFF82; } diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index ab0149fce0a0..47341cd154d7 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -24,7 +24,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.MergedConfiguration; import android.view.DragEvent; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindow; import android.view.IWindowSession; import android.view.InsetsSourceControl; @@ -159,9 +159,9 @@ public class BaseIWindow extends IWindow.Stub { } @Override - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { + public void requestScrollCapture(IScrollCaptureResponseListener listener) { try { - callbacks.onScrollCaptureResponse( + listener.onScrollCaptureResponse( new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build()); } catch (RemoteException ex) { diff --git a/core/java/com/android/internal/view/inline/OWNERS b/core/java/com/android/internal/view/inline/OWNERS new file mode 100644 index 000000000000..edfb2112198a --- /dev/null +++ b/core/java/com/android/internal/view/inline/OWNERS @@ -0,0 +1 @@ +include /core/java/android/view/autofill/OWNERS diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java index 89a90e944bdf..17ce75e63b6f 100644 --- a/core/java/com/android/internal/widget/RecyclerView.java +++ b/core/java/com/android/internal/widget/RecyclerView.java @@ -16,10 +16,16 @@ package com.android.internal.widget; +import static android.widget.EdgeEffect.TYPE_GLOW; +import static android.widget.EdgeEffect.TYPE_STRETCH; +import static android.widget.EdgeEffect.USE_STRETCH_EDGE_EFFECT_BY_DEFAULT; +import static android.widget.EdgeEffect.USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED; + import android.annotation.CallSuper; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.compat.Compatibility; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; @@ -460,6 +466,8 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro private final int[] mScrollConsumed = new int[2]; private final int[] mNestedOffsets = new int[2]; + private int mEdgeEffectType; + /** * These are views that had their a11y importance changed during a layout. We defer these events * until the end of the layout because a11y service may make sync calls back to the RV while @@ -587,6 +595,14 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); } + boolean defaultToStretch = Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_BY_DEFAULT) + || Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED); + TypedArray a = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.EdgeEffect); + mEdgeEffectType = a.getInt(com.android.internal.R.styleable.EdgeEffect_edgeEffectType, + defaultToStretch ? TYPE_STRETCH : TYPE_GLOW); + a.recycle(); + // Re-set whether nested scrolling is enabled so that it is set on all API levels setNestedScrollingEnabled(nestedScrollingEnabled); } @@ -610,6 +626,28 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro } /** + * Returns the {@link EdgeEffect#getType()} used for all EdgeEffects. + * + * @return @link EdgeEffect#getType()} used for all EdgeEffects. + */ + @EdgeEffect.EdgeEffectType + public int getEdgeEffectType() { + return mEdgeEffectType; + } + + /** + * Sets the {@link EdgeEffect#getType()} used in all EdgeEffects. + * Any existing over-scroll effects are cleared and new effects are created as needed. + * + * @param type the {@link EdgeEffect#getType()} used in all EdgeEffects. + */ + public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) { + mEdgeEffectType = type; + invalidateGlows(); + invalidate(); + } + + /** * Instantiate and set a LayoutManager, if specified in the attributes. */ private void createLayoutManager(Context context, String className, AttributeSet attrs, @@ -2183,6 +2221,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro return; } mLeftGlow = new EdgeEffect(getContext()); + mLeftGlow.setType(mEdgeEffectType); if (mClipToPadding) { mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), getMeasuredWidth() - getPaddingLeft() - getPaddingRight()); @@ -2196,6 +2235,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro return; } mRightGlow = new EdgeEffect(getContext()); + mRightGlow.setType(mEdgeEffectType); if (mClipToPadding) { mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), getMeasuredWidth() - getPaddingLeft() - getPaddingRight()); @@ -2209,6 +2249,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro return; } mTopGlow = new EdgeEffect(getContext()); + mTopGlow.setType(mEdgeEffectType); if (mClipToPadding) { mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), getMeasuredHeight() - getPaddingTop() - getPaddingBottom()); @@ -2223,6 +2264,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro return; } mBottomGlow = new EdgeEffect(getContext()); + mBottomGlow.setType(mEdgeEffectType); if (mClipToPadding) { mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), getMeasuredHeight() - getPaddingTop() - getPaddingBottom()); @@ -2663,7 +2705,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f); mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f); - if (mScrollState == SCROLL_STATE_SETTLING) { + if (stopGlowAnimations(e) || mScrollState == SCROLL_STATE_SETTLING) { getParent().requestDisallowInterceptTouchEvent(true); setScrollState(SCROLL_STATE_DRAGGING); } @@ -2731,6 +2773,38 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro return mScrollState == SCROLL_STATE_DRAGGING; } + /** + * This stops any edge glow animation that is currently running by applying a + * 0 length pull at the displacement given by the provided MotionEvent. On pre-S devices, + * this method does nothing, allowing any animating edge effect to continue animating and + * returning <code>false</code> always. + * + * @param e The motion event to use to indicate the finger position for the displacement of + * the current pull. + * @return <code>true</code> if any edge effect had an existing effect to be drawn ond the + * animation was stopped or <code>false</code> if no edge effect had a value to display. + */ + private boolean stopGlowAnimations(MotionEvent e) { + boolean stopped = false; + if (mLeftGlow != null && mLeftGlow.getDistance() != 0) { + mLeftGlow.onPullDistance(0, 1 - (e.getY() / getHeight())); + stopped = true; + } + if (mRightGlow != null && mRightGlow.getDistance() != 0) { + mRightGlow.onPullDistance(0, e.getY() / getHeight()); + stopped = true; + } + if (mTopGlow != null && mTopGlow.getDistance() != 0) { + mTopGlow.onPullDistance(0, e.getX() / getWidth()); + stopped = true; + } + if (mBottomGlow != null && mBottomGlow.getDistance() != 0) { + mBottomGlow.onPullDistance(0, 1 - e.getX() / getWidth()); + stopped = true; + } + return stopped; + } + @Override public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { final int listenerCount = mOnItemTouchListeners.size(); @@ -2807,6 +2881,8 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro final int y = (int) (e.getY(index) + 0.5f); int dx = mLastTouchX - x; int dy = mLastTouchY - y; + dx -= releaseHorizontalGlow(dx, e.getY()); + dy -= releaseVerticalGlow(dy, e.getX()); if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) { dx -= mScrollConsumed[0]; @@ -2887,6 +2963,72 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro return true; } + /** + * If either of the horizontal edge glows are currently active, this consumes part or all of + * deltaX on the edge glow. + * + * @param deltaX The pointer motion, in pixels, in the horizontal direction, positive + * for moving down and negative for moving up. + * @param y The vertical position of the pointer. + * @return The amount of <code>deltaX</code> that has been consumed by the + * edge glow. + */ + private int releaseHorizontalGlow(int deltaX, float y) { + // First allow releasing existing overscroll effect: + float consumed = 0; + float displacement = y / getHeight(); + float pullDistance = (float) deltaX / getWidth(); + if (mLeftGlow != null && mLeftGlow.getDistance() != 0) { + consumed = -mLeftGlow.onPullDistance(-pullDistance, 1 - displacement); + if (mLeftGlow.getDistance() == 0) { + mLeftGlow.onRelease(); + } + } else if (mRightGlow != null && mRightGlow.getDistance() != 0) { + consumed = mRightGlow.onPullDistance(pullDistance, displacement); + if (mRightGlow.getDistance() == 0) { + mRightGlow.onRelease(); + } + } + int pixelsConsumed = Math.round(consumed * getWidth()); + if (pixelsConsumed != 0) { + invalidate(); + } + return pixelsConsumed; + } + + /** + * If either of the vertical edge glows are currently active, this consumes part or all of + * deltaY on the edge glow. + * + * @param deltaY The pointer motion, in pixels, in the vertical direction, positive + * for moving down and negative for moving up. + * @param x The vertical position of the pointer. + * @return The amount of <code>deltaY</code> that has been consumed by the + * edge glow. + */ + private int releaseVerticalGlow(int deltaY, float x) { + // First allow releasing existing overscroll effect: + float consumed = 0; + float displacement = x / getWidth(); + float pullDistance = (float) deltaY / getHeight(); + if (mTopGlow != null && mTopGlow.getDistance() != 0) { + consumed = -mTopGlow.onPullDistance(-pullDistance, displacement); + if (mTopGlow.getDistance() == 0) { + mTopGlow.onRelease(); + } + } else if (mBottomGlow != null && mBottomGlow.getDistance() != 0) { + consumed = mBottomGlow.onPullDistance(pullDistance, 1 - displacement); + if (mBottomGlow.getDistance() == 0) { + mBottomGlow.onRelease(); + } + } + int pixelsConsumed = Math.round(consumed * getHeight()); + if (pixelsConsumed != 0) { + invalidate(); + } + return pixelsConsumed; + } + private void resetTouch() { if (mVelocityTracker != null) { mVelocityTracker.clear(); diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index ab6633f395a8..bfeb01d22bdf 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -45,12 +45,6 @@ static const char* toString(bool value) { return value ? "true" : "false"; } -enum class HandleEventResponse : int { - // Allowed return values of 'handleEvent' function as documented in LooperCallback::handleEvent - REMOVE_CALLBACK = 0, - KEEP_CALLBACK = 1 -}; - static struct { jclass clazz; @@ -77,14 +71,6 @@ static std::string addPrefix(std::string str, std::string_view prefix) { return str; } -/** - * Convert an enumeration to its underlying type. Replace with std::to_underlying when available. - */ -template <class T> -static std::underlying_type_t<T> toUnderlying(const T& t) { - return static_cast<std::underlying_type_t<T>>(t); -} - class NativeInputEventReceiver : public LooperCallback { public: NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak, @@ -121,16 +107,11 @@ private: return mInputConsumer.getChannel()->getName(); } - HandleEventResponse processOutboundEvents(); + status_t processOutboundEvents(); // From 'LooperCallback' int handleEvent(int receiveFd, int events, void* data) override; }; -// Ensure HandleEventResponse underlying type matches the return type of LooperCallback::handleEvent -static_assert(std::is_same<std::underlying_type_t<HandleEventResponse>, - std::invoke_result_t<decltype(&LooperCallback::handleEvent), - NativeInputEventReceiver, int, int, void*>>::value); - NativeInputEventReceiver::NativeInputEventReceiver( JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel, const sp<MessageQueue>& messageQueue) @@ -167,26 +148,12 @@ status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) ALOGD("channel '%s' ~ Finished input event.", getInputChannelName().c_str()); } - status_t status = mInputConsumer.sendFinishedSignal(seq, handled); - if (status != OK) { - if (status == WOULD_BLOCK) { - if (kDebugDispatchCycle) { - ALOGD("channel '%s' ~ Could not send finished signal immediately. " - "Enqueued for later.", getInputChannelName().c_str()); - } - Finish finish; - finish.seq = seq; - finish.handled = handled; - mFinishQueue.push_back(finish); - if (mFinishQueue.size() == 1) { - setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT); - } - return OK; - } - ALOGW("Failed to send finished signal on channel '%s'. status=%d", - getInputChannelName().c_str(), status); - } - return status; + Finish finish{ + .seq = seq, + .handled = handled, + }; + mFinishQueue.push_back(finish); + return processOutboundEvents(); } void NativeInputEventReceiver::setFdEvents(int events) { @@ -217,7 +184,7 @@ void NativeInputEventReceiver::setFdEvents(int events) { * InputPublisher. If no events are remaining, let the looper know so that it doesn't wake up * unnecessarily. */ -HandleEventResponse NativeInputEventReceiver::processOutboundEvents() { +status_t NativeInputEventReceiver::processOutboundEvents() { while (!mFinishQueue.empty()) { const Finish& finish = *mFinishQueue.begin(); status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled); @@ -233,7 +200,8 @@ HandleEventResponse NativeInputEventReceiver::processOutboundEvents() { ALOGD("channel '%s' ~ Remaining outbound events: %zu.", getInputChannelName().c_str(), mFinishQueue.size()); } - return HandleEventResponse::KEEP_CALLBACK; // try again later + setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT); + return WOULD_BLOCK; // try again later } // Some other error. Give up @@ -247,42 +215,49 @@ HandleEventResponse NativeInputEventReceiver::processOutboundEvents() { jniThrowRuntimeException(env, message.c_str()); mMessageQueue->raiseAndClearException(env, "finishInputEvent"); } - return HandleEventResponse::REMOVE_CALLBACK; + return status; } // The queue is now empty. Tell looper there's no more output to expect. setFdEvents(ALOOPER_EVENT_INPUT); - return HandleEventResponse::KEEP_CALLBACK; + return OK; } int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { + // Allowed return values of this function as documented in LooperCallback::handleEvent + constexpr int REMOVE_CALLBACK = 0; + constexpr int KEEP_CALLBACK = 1; + if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { // This error typically occurs when the publisher has closed the input channel // as part of removing a window or finishing an IME session, in which case // the consumer will soon be disposed as well. if (kDebugDispatchCycle) { - ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. " - "events=0x%x", getInputChannelName().c_str(), events); + ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. events=0x%x", + getInputChannelName().c_str(), events); } - return toUnderlying(HandleEventResponse::REMOVE_CALLBACK); + return REMOVE_CALLBACK; } if (events & ALOOPER_EVENT_INPUT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr); mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); - return status == OK || status == NO_MEMORY - ? toUnderlying(HandleEventResponse::KEEP_CALLBACK) - : toUnderlying(HandleEventResponse::REMOVE_CALLBACK); + return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK; } if (events & ALOOPER_EVENT_OUTPUT) { - return toUnderlying(processOutboundEvents()); + const status_t status = processOutboundEvents(); + if (status == OK || status == WOULD_BLOCK) { + return KEEP_CALLBACK; + } else { + return REMOVE_CALLBACK; + } } - ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " - "events=0x%x", getInputChannelName().c_str(), events); - return toUnderlying(HandleEventResponse::KEEP_CALLBACK); + ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. events=0x%x", + getInputChannelName().c_str(), events); + return KEEP_CALLBACK; } status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, @@ -503,9 +478,13 @@ static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr, sp<NativeInputEventReceiver> receiver = reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); status_t status = receiver->finishInputEvent(seq, handled); - if (status && status != DEAD_OBJECT) { + if (status == OK || status == WOULD_BLOCK) { + return; // normal operation + } + if (status != DEAD_OBJECT) { std::string message = - android::base::StringPrintf("Failed to finish input event. status=%d", status); + android::base::StringPrintf("Failed to finish input event. status=%s(%d)", + strerror(-status), status); jniThrowRuntimeException(env, message.c_str()); } } diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 97fdb43bb4f6..52d21a858d4f 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -172,16 +172,16 @@ int NativeInputEventSender::handleEvent(int receiveFd, int events, void* data) { // as part of finishing an IME session, in which case the publisher will // soon be disposed as well. if (kDebugDispatchCycle) { - ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred. " - "events=0x%x", getInputChannelName().c_str(), events); + ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x", + getInputChannelName().c_str(), events); } return 0; // remove the callback } if (!(events & ALOOPER_EVENT_INPUT)) { - ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " - "events=0x%x", getInputChannelName().c_str(), events); + ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. events=0x%x", + getInputChannelName().c_str(), events); return 1; } @@ -219,8 +219,7 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { mPublishedSeqMap.erase(it); if (kDebugDispatchCycle) { - ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, " - "pendingEvents=%zu.", + ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, pendingEvents=%zu.", getInputChannelName().c_str(), seq, result->handled ? "true" : "false", mPublishedSeqMap.size()); } @@ -229,8 +228,8 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { if (!senderObj.get()) { senderObj.reset(jniGetReferent(env, mSenderWeakGlobal)); if (!senderObj.get()) { - ALOGW("channel '%s' ~ Sender object was finalized " - "without being disposed.", getInputChannelName().c_str()); + ALOGW("channel '%s' ~ Sender object was finalized without being disposed.", + getInputChannelName().c_str()); return DEAD_OBJECT; } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index cbf4481bd2f1..65b8b988f38b 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -746,7 +746,7 @@ static void nativeSetWindowCrop(JNIEnv* env, jclass clazz, jlong transactionObj, SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); Rect crop(l, t, r, b); - transaction->setCrop_legacy(ctrl, crop); + transaction->setCrop(ctrl, crop); } static void nativeSetCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj, @@ -1528,11 +1528,17 @@ static jboolean nativeGetDisplayBrightnessSupport(JNIEnv* env, jclass clazz, } static jboolean nativeSetDisplayBrightness(JNIEnv* env, jclass clazz, jobject displayTokenObject, - jfloat brightness) { + jfloat sdrBrightness, jfloat sdrBrightnessNits, + jfloat displayBrightness, jfloat displayBrightnessNits) { sp<IBinder> displayToken(ibinderForJavaObject(env, displayTokenObject)); if (displayToken == nullptr) { return JNI_FALSE; } + gui::DisplayBrightness brightness; + brightness.sdrWhitePoint = sdrBrightness; + brightness.sdrWhitePointNits = sdrBrightnessNits; + brightness.displayBrightness = displayBrightness; + brightness.displayBrightnessNits = displayBrightnessNits; status_t error = SurfaceComposerClient::setDisplayBrightness(displayToken, brightness); return error == OK ? JNI_TRUE : JNI_FALSE; } @@ -1860,7 +1866,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSyncInputWindows }, {"nativeGetDisplayBrightnessSupport", "(Landroid/os/IBinder;)Z", (void*)nativeGetDisplayBrightnessSupport }, - {"nativeSetDisplayBrightness", "(Landroid/os/IBinder;F)Z", + {"nativeSetDisplayBrightness", "(Landroid/os/IBinder;FFFF)Z", (void*)nativeSetDisplayBrightness }, {"nativeReadTransactionFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadTransactionFromParcel }, diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 2b665c0fe9fc..a5fbae9878ac 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -513,17 +513,17 @@ message IncidentProto { optional com.android.server.powerstats.PowerStatsServiceMeterProto powerstats_meter = 3054 [ (section).type = SECTION_DUMPSYS, - (section).args = "power_stats --proto meter" + (section).args = "powerstats --proto meter" ]; optional com.android.server.powerstats.PowerStatsServiceModelProto powerstats_model = 3055 [ (section).type = SECTION_DUMPSYS, - (section).args = "power_stats --proto model" + (section).args = "powerstats --proto model" ]; optional com.android.server.powerstats.PowerStatsServiceResidencyProto powerstats_residency = 3056 [ (section).type = SECTION_DUMPSYS, - (section).args = "power_stats --proto residency" + (section).args = "powerstats --proto residency" ]; // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999 diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto index e32c07f50ff2..3a959f13fea4 100644 --- a/core/proto/android/server/usagestatsservice.proto +++ b/core/proto/android/server/usagestatsservice.proto @@ -57,6 +57,8 @@ message IntervalStatsProto { // Time attributes stored as an offset of the IntervalStats's beginTime. optional int64 last_time_visible_ms = 10; optional int64 total_time_visible_ms = 11; + // Time attributes stored as an offset of the IntervalStats's beginTime. + optional int64 last_time_component_used_ms = 12; } // Stores the relevant information an IntervalStats will have about a Configuration diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto index 664c22de86a6..3e5cd92c2533 100644 --- a/core/proto/android/server/usagestatsservice_v2.proto +++ b/core/proto/android/server/usagestatsservice_v2.proto @@ -81,6 +81,7 @@ message UsageStatsObfuscatedProto { optional int64 total_time_service_used_ms = 9; optional int64 last_time_visible_ms = 10; optional int64 total_time_visible_ms = 11; + optional int64 last_time_component_used_ms = 12; } /** diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index f92276171b26..521d246dc0dc 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -987,6 +987,23 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:protectionLevel="signature|appop|preinstalled" /> + <!-- Allows an application to modify and delete media files on this device or any connected + storage device without user confirmation. Applications must already be granted the + {@link #READ_EXTERNAL_STORAGE} or {@link #MANAGE_EXTERNAL_STORAGE}} permissions for this + permission to take effect. + <p>Even if applications are granted this permission, if applications want to modify or + delete media files, they also must get the access by calling + {@link android.provider.MediaStore#createWriteRequest(ContentResolver, Collection)}, + {@link android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection)}, or + {@link android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean)}. + <p>This permission doesn't give read or write access directly. It only prevents the user + confirmation dialog for these requests. + <p>If applications are not granted {@link #ACCESS_MEDIA_LOCATION}, the system also pops up + the user confirmation dialog for the write request. + <p>Protection level: signature|appop|preinstalled --> + <permission android:name="android.permission.MANAGE_MEDIA" + android:protectionLevel="signature|appop|preinstalled" /> + <!-- ====================================================================== --> <!-- Permissions for accessing the device location --> <!-- ====================================================================== --> @@ -2775,11 +2792,11 @@ The app can check whether it has this authorization by calling {@link android.provider.Settings#canDrawOverlays Settings.canDrawOverlays()}. - <p>Protection level: signature|appop|installer|appPredictor|pre23|development --> + <p>Protection level: signature|setup|appop|installer|appPredictor|pre23|development --> <permission android:name="android.permission.SYSTEM_ALERT_WINDOW" android:label="@string/permlab_systemAlertWindow" android:description="@string/permdesc_systemAlertWindow" - android:protectionLevel="signature|appop|installer|appPredictor|pre23|development" /> + android:protectionLevel="signature|setup|appop|installer|appPredictor|pre23|development" /> <!-- @SystemApi @hide Allows an application to create windows using the type {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}, @@ -4365,6 +4382,11 @@ <permission android:name="android.permission.MODIFY_AUDIO_ROUTING" android:protectionLevel="signature|privileged" /> + <!-- @TestApi Allows an application to query audio related state. + @hide --> + <permission android:name="android.permission.QUERY_AUDIO_STATE" + android:protectionLevel="signature" /> + <!-- Allows an application to modify what effects are applied to all audio (matching certain criteria) from any application. <p>Not for use by third-party applications.</p> @@ -5602,6 +5624,10 @@ <!-- Attribution for Gnss Time Update service. --> <attribution android:tag="GnssTimeUpdateService" android:label="@string/gnss_time_update_service"/> + <!-- Attribution for MusicRecognitionManagerService. + <p>Not for use by third-party applications.</p> --> + <attribution android:tag="MusicRecognitionManagerService" + android:label="@string/music_recognition_manager_service"/> <application android:process="system" android:persistent="true" diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 7bc4663d1070..c3b35c81cb66 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -252,5 +252,8 @@ <item type="id" name="remote_views_next_child" /> <!-- View tag associating a view with its stable id for potential recycling. --> - <item type="id" name = "remote_views_stable_id" /> + <item type="id" name="remote_views_stable_id" /> + + <!-- View tag associating a view with its overridden id, to ensure valid recycling only. --> + <item type="id" name="remote_views_override_id" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2c7f9a4a5ffe..054d1080f4d4 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -453,6 +453,9 @@ <!-- Attribution for Gnss Time Update service. [CHAR LIMIT=NONE]--> <string name="gnss_time_update_service">GNSS Time Update Service</string> + <!-- Attribution for MusicRecognitionManagerService. [CHAR LIMIT=NONE]--> + <string name="music_recognition_manager_service">Music Recognition Manager Service</string> + <!-- Factory reset warning dialog strings--> <skip /> <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] --> <string name="factory_reset_warning">Your device will be erased</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b9357885175d..ef5191af2e6b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4322,4 +4322,5 @@ <java-symbol type="id" name="remote_views_next_child" /> <java-symbol type="id" name="remote_views_stable_id" /> + <java-symbol type="id" name="remote_views_override_id" /> </resources> diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java index fe31b907f077..7ef1d5e426cc 100644 --- a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java +++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java @@ -61,7 +61,7 @@ public class AppSearchSessionUnitTest { public void testPutDocument_throwsNullException() throws Exception { // Create a document AppSearchEmail inEmail = - new AppSearchEmail.Builder("uri1") + new AppSearchEmail.Builder("namespace", "uri1") .setFrom("from@example.com") .setTo("to1@example.com", "to2@example.com") .setSubject("testPut example") diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java index 119b70ab0439..ed53d5f39a39 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java @@ -25,7 +25,7 @@ public class AppSearchEmailTest { @Test public void testBuildEmailAndGetValue() { AppSearchEmail email = - new AppSearchEmail.Builder("uri") + new AppSearchEmail.Builder("namespace", "uri") .setFrom("FakeFromAddress") .setCc("CC1", "CC2") // Score and Property are mixed into the middle to make sure @@ -37,6 +37,7 @@ public class AppSearchEmailTest { .setBody("EmailBody") .build(); + assertThat(email.getNamespace()).isEqualTo("namespace"); assertThat(email.getUri()).isEqualTo("uri"); assertThat(email.getFrom()).isEqualTo("FakeFromAddress"); assertThat(email.getTo()).isNull(); diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java index af77b6c598b1..b884ddcd1420 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java @@ -27,13 +27,13 @@ public class GenericDocumentTest { @Test public void testRecreateFromParcel() { GenericDocument inDoc = - new GenericDocument.Builder<>("uri1", "schema1") + new GenericDocument.Builder<>("namespace", "uri1", "schema1") .setScore(42) .setPropertyString("propString", "Hello") .setPropertyBytes("propBytes", new byte[][] {{1, 2}}) .setPropertyDocument( "propDocument", - new GenericDocument.Builder<>("uri2", "schema2") + new GenericDocument.Builder<>("namespace", "uri2", "schema2") .setPropertyString("propString", "Goodbye") .setPropertyBytes("propBytes", new byte[][] {{3, 4}}) .build()) diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java index 7d175d9b31e5..76372141ca04 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java @@ -31,8 +31,8 @@ public class PutDocumentsRequestTest { public void addGenericDocument_byCollection() { Set<AppSearchEmail> emails = ImmutableSet.of( - new AppSearchEmail.Builder("test1").build(), - new AppSearchEmail.Builder("test2").build()); + new AppSearchEmail.Builder("namespace", "test1").build(), + new AppSearchEmail.Builder("namespace", "test2").build()); PutDocumentsRequest request = new PutDocumentsRequest.Builder().addGenericDocuments(emails).build(); diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 6c8b94129c82..9915e3852b8d 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -133,7 +133,7 @@ public class ObjectPoolTests { int ident = 57; ActivityInfo activityInfo = new ActivityInfo(); activityInfo.flags = 42; - activityInfo.maxAspectRatio = 2.4f; + activityInfo.setMaxAspectRatio(2.4f); activityInfo.launchToken = "token"; activityInfo.applicationInfo = new ApplicationInfo(); activityInfo.packageName = "packageName"; diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 6f3d7ae5eb3c..f47fa39e7dc8 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -182,7 +182,7 @@ public class TransactionParcelTests { int ident = 57; ActivityInfo activityInfo = new ActivityInfo(); activityInfo.flags = 42; - activityInfo.maxAspectRatio = 2.4f; + activityInfo.setMaxAspectRatio(2.4f); activityInfo.launchToken = "token"; activityInfo.applicationInfo = new ApplicationInfo(); activityInfo.packageName = "packageName"; diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java index 8de9454ddeda..083e37a3aaa6 100644 --- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java +++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java @@ -46,7 +46,8 @@ public class UsageStatsPersistenceTest { private static final String[] USAGESTATS_PERSISTED_FIELDS = {"mBeginTimeStamp", "mEndTimeStamp", "mPackageName", "mPackageToken", "mLastEvent", "mAppLaunchCount", "mChooserCounts", "mLastTimeUsed", "mTotalTimeInForeground", "mLastTimeForegroundServiceUsed", - "mTotalTimeForegroundServiceUsed", "mLastTimeVisible", "mTotalTimeVisible"}; + "mTotalTimeForegroundServiceUsed", "mLastTimeVisible", "mTotalTimeVisible", + "mLastTimeComponentUsed"}; // All fields in this list are defined in UsageStats but not persisted private static final String[] USAGESTATS_IGNORED_FIELDS = {"CREATOR", "mActivities", "mForegroundServices", "mLaunchCount", "mChooserCountsObfuscated"}; diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java index 0ac00b8e9bbc..858bbd20f13d 100644 --- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java +++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java @@ -20,6 +20,7 @@ import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED; import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; +import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED; import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.END_OF_DAY; @@ -137,6 +138,7 @@ public class UsageStatsTest { left.mPackageName = "com.test"; left.mBeginTimeStamp = 100000; left.mTotalTimeInForeground = 10; + left.mLastTimeComponentUsed = 200000; left.mActivities.put(1, Event.ACTIVITY_RESUMED); left.mActivities.put(2, Event.ACTIVITY_RESUMED); @@ -542,6 +544,19 @@ public class UsageStatsTest { } @Test + public void testEvent_APP_COMPONENT_USED() { + left.mPackageName = "com.test"; + left.mBeginTimeStamp = 100000; + final String className = "com.test.component1"; + + left.update(className, 200000, APP_COMPONENT_USED, 0); + assertEquals(left.mLastTimeComponentUsed, 200000); + + left.update(className, 300000, APP_COMPONENT_USED, 0); + assertEquals(left.mLastTimeComponentUsed, 300000); + } + + @Test public void testEvent_DEVICE_SHUTDOWN() { testClosingEvent(DEVICE_SHUTDOWN); } @@ -586,6 +601,7 @@ public class UsageStatsTest { assertEquals(us1.mBeginTimeStamp, us2.mBeginTimeStamp); assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed); assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible); + assertEquals(us1.mLastTimeComponentUsed, us2.mLastTimeComponentUsed); assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed); assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground); assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed); diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java index 516fb76eeaf7..f3a6f9e9de17 100644 --- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java +++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java @@ -74,7 +74,7 @@ public class ScrollCaptureConnectionTest { mTarget = new ScrollCaptureTarget(mView, mLocalVisibleRect, mPositionInWindow, mCallback); mTarget.setScrollBounds(mScrollBounds); - mConnection = new ScrollCaptureConnection(Runnable::run, mTarget, mRemote); + mConnection = new ScrollCaptureConnection(Runnable::run, mTarget); } /** Test creating a client with valid info */ @@ -83,7 +83,7 @@ public class ScrollCaptureConnectionTest { ScrollCaptureTarget target = new ScrollCaptureTarget( mView, mLocalVisibleRect, mPositionInWindow, mCallback); target.setScrollBounds(new Rect(1, 2, 3, 4)); - new ScrollCaptureConnection(Runnable::run, target, mRemote); + new ScrollCaptureConnection(Runnable::run, target); } /** Test creating a client fails if arguments are not valid. */ @@ -91,20 +91,20 @@ public class ScrollCaptureConnectionTest { public void testConstruction_requiresScrollBounds() { try { mTarget.setScrollBounds(null); - new ScrollCaptureConnection(Runnable::run, mTarget, mRemote); + new ScrollCaptureConnection(Runnable::run, mTarget); fail("An exception was expected."); } catch (RuntimeException ex) { // Ignore, expected. } } - /** @see ScrollCaptureConnection#startCapture(Surface) */ + /** @see ScrollCaptureConnection#startCapture(Surface, IScrollCaptureCallbacks) */ @Test public void testStartCapture() throws Exception { - mConnection.startCapture(mSurface); + mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); - assertTrue(mConnection.isStarted()); + assertTrue(mConnection.isActive()); verify(mRemote, times(1)).onCaptureStarted(); verifyNoMoreInteractions(mRemote); @@ -112,11 +112,11 @@ public class ScrollCaptureConnectionTest { @Test public void testStartCapture_cancellation() throws Exception { - ICancellationSignal signal = mConnection.startCapture(mSurface); + ICancellationSignal signal = mConnection.startCapture(mSurface, mRemote); signal.cancel(); mCallback.completeStartRequest(); - assertFalse(mConnection.isStarted()); + assertFalse(mConnection.isActive()); verifyNoMoreInteractions(mRemote); } @@ -124,7 +124,7 @@ public class ScrollCaptureConnectionTest { /** @see ScrollCaptureConnection#requestImage(Rect) */ @Test public void testRequestImage() throws Exception { - mConnection.startCapture(mSurface); + mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); reset(mRemote); @@ -138,7 +138,7 @@ public class ScrollCaptureConnectionTest { @Test public void testRequestImage_cancellation() throws Exception { - mConnection.startCapture(mSurface); + mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); reset(mRemote); @@ -152,7 +152,7 @@ public class ScrollCaptureConnectionTest { /** @see ScrollCaptureConnection#endCapture() */ @Test public void testEndCapture() throws Exception { - mConnection.startCapture(mSurface); + mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); reset(mRemote); @@ -167,7 +167,7 @@ public class ScrollCaptureConnectionTest { /** @see ScrollCaptureConnection#endCapture() */ @Test public void testEndCapture_cancellation() throws Exception { - mConnection.startCapture(mSurface); + mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); reset(mRemote); @@ -179,9 +179,9 @@ public class ScrollCaptureConnectionTest { } @Test - public void testClose() throws Exception { + public void testClose() { mConnection.close(); - assertFalse(mConnection.isConnected()); + assertFalse(mConnection.isActive()); verifyNoMoreInteractions(mRemote); } diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 7746bc2e273a..e0d9ecfcb336 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -205,7 +205,7 @@ public class ViewRootImplTest { @Test public void requestScrollCapture_withoutContentRoot() { final CountDownLatch latch = new CountDownLatch(1); - mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() { + mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureResponseListener.Default() { @Override public void onScrollCaptureResponse(ScrollCaptureResponse response) { latch.countDown(); @@ -237,7 +237,7 @@ public class ViewRootImplTest { final CountDownLatch latch = new CountDownLatch(1); mViewRootImpl.setScrollCaptureRequestTimeout(100); - mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() { + mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureResponseListener.Default() { @Override public void onScrollCaptureResponse(ScrollCaptureResponse response) { latch.countDown(); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index ee472880b79f..46e2772b30ca 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -60,6 +60,7 @@ import org.junit.runners.Suite; KernelCpuUidFreqTimeReaderTest.class, KernelCpuUidUserSysTimeReaderTest.class, KernelMemoryBandwidthStatsTest.class, + KernelSingleProcessCpuThreadReaderTest.class, KernelSingleUidTimeReaderTest.class, KernelWakelockReaderTest.class, LongSamplingCounterTest.class, @@ -69,6 +70,7 @@ import org.junit.runners.Suite; PowerProfileTest.class, ScreenPowerCalculatorTest.class, SensorPowerCalculatorTest.class, + SystemServerCpuThreadReaderTest.class, SystemServicePowerCalculatorTest.class, UserPowerCalculatorTest.class, VideoPowerCalculatorTest.class, diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java index 8aeb761ffc4d..80ab36ec84cf 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -33,11 +33,15 @@ import android.util.SparseArray; import androidx.test.InstrumentationRegistry; +import com.android.internal.power.MeasuredEnergyStats; + import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.mockito.stubbing.Answer; +import java.util.Arrays; + public class BatteryUsageStatsRule implements TestRule { private final PowerProfile mPowerProfile; private final MockClocks mMockClocks = new MockClocks(); @@ -98,6 +102,16 @@ public class BatteryUsageStatsRule implements TestRule { return this; } + /** Call only after setting the power profile information. */ + public BatteryUsageStatsRule initMeasuredEnergyStatsLocked(int numCustom) { + final boolean[] supportedStandardBuckets = + new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; + Arrays.fill(supportedStandardBuckets, true); + mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets, numCustom); + mBatteryStats.informThatAllExternalStatsAreFlushed(); + return this; + } + public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) { mScreenOn = screenOn; return this; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java index 23ea508d19d3..33b8aedb7970 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -35,7 +35,6 @@ import org.junit.runner.RunWith; import java.util.List; @SmallTest -@SkipPresubmit("b/180015146") @RunWith(AndroidJUnit4.class) public class BatteryUsageStatsTest { @@ -102,7 +101,7 @@ public class BatteryUsageStatsTest { } public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) { - assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(100); + assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(21500); assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20); assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000); assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000); @@ -128,7 +127,7 @@ public class BatteryUsageStatsTest { BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND)).isEqualTo(700); assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis( BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800); - assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1710); + assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1200); } else { fail("Unexpected UID " + uidBatteryConsumer.getUid()); } @@ -146,7 +145,7 @@ public class BatteryUsageStatsTest { BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300); assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis( BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400); - assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(30510); + assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(20300); } else { fail("Unexpected drain type " + systemBatteryConsumer.getDrainType()); } diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java index f6aa08bf0645..71d7668bbcfa 100644 --- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.annotation.Nullable; import android.os.BatteryConsumer; +import android.os.BatteryUsageStatsQuery; import android.os.Process; import android.os.SystemBatteryConsumer; @@ -43,7 +44,6 @@ public class BluetoothPowerCalculatorTest { .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0); @Test - @SkipPresubmit("b/180015146") public void testTimerBasedModel() { setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID) .getOrCreateBluetoothControllerActivityLocked(), @@ -60,11 +60,10 @@ public class BluetoothPowerCalculatorTest { BluetoothPowerCalculator calculator = new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); - mStatsRule.apply(calculator); + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); - assertBluetoothPowerAndDuration( - mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.11388, 6000); + assertThat(mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID)).isNull(); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), 0.24722, 15000); @@ -74,7 +73,6 @@ public class BluetoothPowerCalculatorTest { } @Test - @SkipPresubmit("b/180015146") public void testReportedPowerBasedModel() { setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID) .getOrCreateBluetoothControllerActivityLocked(), @@ -91,11 +89,10 @@ public class BluetoothPowerCalculatorTest { BluetoothPowerCalculator calculator = new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); - mStatsRule.apply(calculator); + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); - assertBluetoothPowerAndDuration( - mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.1, 6000); + assertThat(mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID)).isNull(); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), 0.2, 15000); diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java index 10ff3a47a7d9..496415a43a6a 100644 --- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java @@ -101,7 +101,6 @@ public class CpuPowerCalculatorTest { } @Test - @SkipPresubmit("b/180015146") public void testTimerBasedModel() { when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); diff --git a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java index 0c91b2959f8e..f0111171b83c 100644 --- a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java @@ -42,9 +42,12 @@ public class CustomMeasuredPowerCalculatorTest { public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); @Test - @SkipPresubmit("b/180015146") public void testMeasuredEnergyCopiedIntoBatteryConsumers() { final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + // For side-effect of creating a BatteryStats.Uid + batteryStats.getUidStatsLocked(APP_UID); + SparseLongArray uidEnergies = new SparseLongArray(); uidEnergies.put(APP_UID, 30_000_000); batteryStats.updateCustomMeasuredEnergyStatsLocked(0, 100_000_000, uidEnergies); @@ -60,18 +63,18 @@ public class CustomMeasuredPowerCalculatorTest { UidBatteryConsumer uid = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uid.getConsumedPowerForCustomComponent( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)) - .isWithin(PRECISION).of(2.252252); + .isWithin(PRECISION).of(8.333333); assertThat(uid.getConsumedPowerForCustomComponent( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1)) - .isWithin(PRECISION).of(9.009009); + .isWithin(PRECISION).of(33.33333); SystemBatteryConsumer systemConsumer = mStatsRule.getSystemBatteryConsumer( SystemBatteryConsumer.DRAIN_TYPE_CUSTOM); assertThat(systemConsumer.getConsumedPowerForCustomComponent( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)) - .isWithin(PRECISION).of(7.5075075); + .isWithin(PRECISION).of(27.77777); assertThat(systemConsumer.getConsumedPowerForCustomComponent( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1)) - .isWithin(PRECISION).of(15.015015); + .isWithin(PRECISION).of(55.55555); } } diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index a47c4d832083..26adbe9e7c59 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -46,6 +46,7 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { initTimersAndCounters(); setExternalStatsSyncLocked(new DummyExternalStatsSync()); + informThatAllExternalStatsAreFlushed(); final boolean[] supportedStandardBuckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java index 6edbbb0ad789..58e2513897ac 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -78,7 +78,6 @@ public class SystemServicePowerCalculatorTest { } @Test - @SkipPresubmit("b/180015146") public void testPowerProfileBasedModel() { when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); @@ -154,7 +153,7 @@ public class SystemServicePowerCalculatorTest { } @Override - public void readDelta(@Nullable Callback<long[]> cb) { + public void readDelta(boolean forcedRead, @Nullable Callback<long[]> cb) { if (cb != null) { cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes); } diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java index e1005457c289..2e23dc8dbba8 100644 --- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java @@ -17,11 +17,14 @@ package com.android.internal.os; +import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; + import static com.google.common.truth.Truth.assertThat; import android.net.NetworkCapabilities; import android.net.NetworkStats; import android.os.BatteryConsumer; +import android.os.BatteryUsageStatsQuery; import android.os.Process; import android.os.SystemBatteryConsumer; import android.os.UidBatteryConsumer; @@ -50,10 +53,11 @@ public class WifiPowerCalculatorTest { .setAveragePower(PowerProfile.POWER_WIFI_ON, 360.0) .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0) .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0) - .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0); + .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0) + .initMeasuredEnergyStatsLocked(0); - @Test - public void testPowerControllerBasedModel() { + /** Sets up a batterystats object with pre-populated network values. */ + private BatteryStatsImpl setupTestNetworkNumbers() { BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); batteryStats.noteNetworkInterfaceForTransports("wifi", @@ -64,13 +68,25 @@ public class WifiPowerCalculatorTest { .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); mStatsRule.setNetworkStats(networkStats); - WifiActivityEnergyInfo energyInfo = new WifiActivityEnergyInfo(10000, + return batteryStats; + } + + /** Sets up an WifiActivityEnergyInfo for ActivityController-model-based tests. */ + private WifiActivityEnergyInfo setupPowerControllerBasedModelEnergyNumbersInfo() { + return new WifiActivityEnergyInfo(10000, WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000); + } + + @Test + public void testPowerControllerBasedModel_nonMeasured() { + final BatteryStatsImpl batteryStats = setupTestNetworkNumbers(); + final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo(); - batteryStats.updateWifiState(energyInfo, 1000, 1000); + batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000); WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); - mStatsRule.apply(calculator); + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) @@ -87,30 +103,54 @@ public class WifiPowerCalculatorTest { } @Test - public void testTimerBasedModel() { - BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + public void testPowerControllerBasedModel_measured() { + final BatteryStatsImpl batteryStats = setupTestNetworkNumbers(); + final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo(); - batteryStats.noteNetworkInterfaceForTransports("wifi", - new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000); - NetworkStats networkStats = new NetworkStats(10000, 1) - .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100) - .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); - mStatsRule.setNetworkStats(networkStats); + WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(calculator); + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(1423); + /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */ + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.2214666 / (0.2214666 + 0.645200) * 1_000_000 / 3600000); + + SystemBatteryConsumer systemConsumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(5577); + /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */ + assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.645200 / (0.2214666 + 0.645200) * 1_000_000 / 3600000); + } + + /** Sets up batterystats object with prepopulated network & timer data for Timer-model tests. */ + private BatteryStatsImpl setupTimerBasedModelTestNumbers() { + final BatteryStatsImpl batteryStats = setupTestNetworkNumbers(); batteryStats.noteWifiScanStartedLocked(APP_UID, 1000, 1000); batteryStats.noteWifiScanStoppedLocked(APP_UID, 2000, 2000); batteryStats.noteWifiRunningLocked(new WorkSource(APP_UID), 3000, 3000); batteryStats.noteWifiStoppedLocked(new WorkSource(APP_UID), 4000, 4000); batteryStats.noteWifiRunningLocked(new WorkSource(Process.WIFI_UID), 1111, 2222); batteryStats.noteWifiStoppedLocked(new WorkSource(Process.WIFI_UID), 3333, 4444); + return batteryStats; + } + + @Test + public void testTimerBasedModel_nonMeasured() { + final BatteryStatsImpl batteryStats = setupTimerBasedModelTestNumbers(); // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively // on the packet counts. - batteryStats.updateWifiState(/* energyInfo */ null, 1000, 1000); + batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000); WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); - mStatsRule.apply(calculator); + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) @@ -125,4 +165,31 @@ public class WifiPowerCalculatorTest { assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.8759216); } + + @Test + public void testTimerBasedModel_measured() { + final BatteryStatsImpl batteryStats = setupTimerBasedModelTestNumbers(); + + // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively + // on the packet counts. + batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000); + + WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(calculator); + + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(1000); + /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */ + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.8231573 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000); + + SystemBatteryConsumer systemConsumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(2222); + /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */ + assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.8759216 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000); + } } diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index 89644e2320c1..0268953eab42 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. - - // Privapp permission whitelist files package { @@ -187,3 +185,17 @@ prebuilt_etc { src: "com.android.car.activityresolver.xml", filename_from_src: true, } + +prebuilt_etc { + name: "allowed_privapp_com.android.car.cluster.home", + sub_dir: "permissions", + src: "com.android.car.cluster.home.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "allowed_privapp_com.android.car.messenger", + sub_dir: "permissions", + src: "com.android.car.messenger.xml", + filename_from_src: true, +} diff --git a/data/etc/car/com.android.car.cluster.home.xml b/data/etc/car/com.android.car.cluster.home.xml new file mode 100644 index 000000000000..4c2d614df5b0 --- /dev/null +++ b/data/etc/car/com.android.car.cluster.home.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.cluster.home"> + <permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.messenger.xml b/data/etc/car/com.android.car.messenger.xml new file mode 100644 index 000000000000..16595c30c65c --- /dev/null +++ b/data/etc/car/com.android.car.messenger.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.messenger"> + <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml index 6132d53b4651..58306be90048 100644 --- a/data/etc/car/com.android.car.shell.xml +++ b/data/etc/car/com.android.car.shell.xml @@ -20,5 +20,11 @@ <privapp-permissions package="com.android.shell"> <permission name="android.permission.INSTALL_PACKAGES" /> <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + <permission name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"/> + <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/> + <permission name="android.car.permission.CAR_DIAGNOSTICS"/> + <permission name="android.car.permission.CAR_DRIVING_STATE"/> + <permission name="android.car.permission.CAR_POWER"/> + <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 4a3bd99b8f7c..115bd9b08a53 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -823,12 +823,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/Task.java" }, - "-1159577965": { - "message": "Focus requested for input consumer=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/InputMonitor.java" - }, "-1156118957": { "message": "Updated config=%s", "level": "DEBUG", @@ -2479,12 +2473,6 @@ "group": "WM_DEBUG_RESIZE", "at": "com\/android\/server\/wm\/WindowState.java" }, - "690411811": { - "message": "goodToGo(): No apps to animate", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, "691515534": { "message": " Commit wallpaper becoming invisible: %s", "level": "VERBOSE", @@ -2605,12 +2593,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.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", @@ -2917,6 +2899,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/WindowState.java" }, + "1305412562": { + "message": "Report configuration: %s %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityClientController.java" + }, "1316533291": { "message": "State movement: %s from:%s to:%s reason:%s", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java index f3bf63bb3660..b57f7af14a0e 100644 --- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java +++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java @@ -38,8 +38,7 @@ import java.util.function.Consumer; */ public final class RippleAnimationSession { private static final String TAG = "RippleAnimationSession"; - private static final int ENTER_ANIM_DURATION = 300; - private static final int SLIDE_ANIM_DURATION = 450; + private static final int ENTER_ANIM_DURATION = 450; private static final int EXIT_ANIM_DURATION = 300; private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); private static final TimeInterpolator PATH_INTERPOLATOR = @@ -50,26 +49,21 @@ public final class RippleAnimationSession { private Runnable mOnUpdate; private long mStartTime; private boolean mForceSoftware; - private final float mWidth, mHeight; private final ValueAnimator mSparkle = ValueAnimator.ofFloat(0, 1); - private final ArraySet<Animator> mActiveAnimations = new ArraySet<>(3); RippleAnimationSession(@NonNull AnimationProperties<Float, Paint> properties, - boolean forceSoftware, float width, float height) { + boolean forceSoftware) { mProperties = properties; mForceSoftware = forceSoftware; - mWidth = width; - mHeight = height; mSparkle.addUpdateListener(anim -> { final long now = AnimationUtils.currentAnimationTimeMillis(); final long elapsed = now - mStartTime - ENTER_ANIM_DURATION; - final float phase = (float) elapsed / 1000f; - mProperties.getShader().setSecondsOffset(phase); + final float phase = (float) elapsed / 30000f; + mProperties.getShader().setNoisePhase(phase); notifyUpdate(); }); mSparkle.setDuration(ENTER_ANIM_DURATION); - mSparkle.setStartDelay(ENTER_ANIM_DURATION); mSparkle.setInterpolator(LINEAR_INTERPOLATOR); mSparkle.setRepeatCount(ValueAnimator.INFINITE); } @@ -85,7 +79,6 @@ public final class RippleAnimationSession { } @NonNull RippleAnimationSession exit(Canvas canvas) { - mSparkle.end(); if (isHwAccelerated(canvas)) exitHardware((RecordingCanvas) canvas); else exitSoftware(); return this; @@ -93,7 +86,6 @@ public final class RippleAnimationSession { private void onAnimationEnd(Animator anim) { notifyUpdate(); - mActiveAnimations.remove(anim); } @NonNull RippleAnimationSession setOnSessionEnd( @@ -123,18 +115,18 @@ public final class RippleAnimationSession { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); + mSparkle.end(); Consumer<RippleAnimationSession> onEnd = mOnSessionEnd; if (onEnd != null) onEnd.accept(RippleAnimationSession.this); } }); expand.setInterpolator(LINEAR_INTERPOLATOR); expand.start(); - mActiveAnimations.add(expand); } private long computeDelay() { final long timePassed = AnimationUtils.currentAnimationTimeMillis() - mStartTime; - return Math.max((long) SLIDE_ANIM_DURATION - timePassed, 0); + return Math.max((long) ENTER_ANIM_DURATION - timePassed, 0); } private void notifyUpdate() { @@ -157,6 +149,7 @@ public final class RippleAnimationSession { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); + mSparkle.end(); Consumer<RippleAnimationSession> onEnd = mOnSessionEnd; if (onEnd != null) onEnd.accept(RippleAnimationSession.this); } @@ -167,7 +160,6 @@ public final class RippleAnimationSession { long delay = computeDelay(); exit.setStartDelay(delay); exit.start(); - mActiveAnimations.add(exit); } private void enterHardware(RecordingCanvas canvas) { @@ -175,54 +167,27 @@ public final class RippleAnimationSession { props = getCanvasProperties(); RenderNodeAnimator expand = new RenderNodeAnimator(props.getProgress(), .5f); - RenderNodeAnimator slideX = - new RenderNodeAnimator(props.getX(), mWidth / 2); - RenderNodeAnimator slideY = - new RenderNodeAnimator(props.getY(), mHeight / 2); expand.setTarget(canvas); - slideX.setTarget(canvas); - slideY.setTarget(canvas); - startAnimation(expand, slideX, slideY); + startAnimation(expand); } - private void startAnimation(Animator expand, - Animator slideX, Animator slideY) { - expand.setDuration(SLIDE_ANIM_DURATION); - slideX.setDuration(SLIDE_ANIM_DURATION); - slideY.setDuration(SLIDE_ANIM_DURATION); - slideX.addListener(new AnimatorListener(this)); + private void startAnimation(Animator expand) { + expand.setDuration(ENTER_ANIM_DURATION); + expand.addListener(new AnimatorListener(this)); expand.setInterpolator(LINEAR_INTERPOLATOR); - slideX.setInterpolator(PATH_INTERPOLATOR); - slideY.setInterpolator(PATH_INTERPOLATOR); expand.start(); - slideX.start(); - slideY.start(); if (!mSparkle.isRunning()) { mSparkle.start(); - mActiveAnimations.add(mSparkle); } - mActiveAnimations.add(expand); - mActiveAnimations.add(slideX); - mActiveAnimations.add(slideY); } private void enterSoftware() { ValueAnimator expand = ValueAnimator.ofFloat(0f, 0.5f); - ValueAnimator slideX = ValueAnimator.ofFloat( - mProperties.getX(), mWidth / 2); - ValueAnimator slideY = ValueAnimator.ofFloat( - mProperties.getY(), mHeight / 2); expand.addUpdateListener(updatedAnimation -> { notifyUpdate(); mProperties.getShader().setProgress((Float) expand.getAnimatedValue()); }); - slideX.addUpdateListener(anim -> { - float x = (float) slideX.getAnimatedValue(); - float y = (float) slideY.getAnimatedValue(); - mProperties.setOrigin(x, y); - mProperties.getShader().setOrigin(x, y); - }); - startAnimation(expand, slideX, slideY); + startAnimation(expand); } @NonNull AnimationProperties<Float, Paint> getProperties() { diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index d6bbee90d73b..fb2b9b343f0a 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -842,7 +842,7 @@ public class RippleDrawable extends LayerDrawable { if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) { RippleAnimationSession.AnimationProperties<Float, Paint> properties = createAnimationProperties(x, y, w, h); - mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps, w, h) + mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps) .setOnAnimationUpdated(() -> invalidateSelf(false)) .setOnSessionEnd(session -> { mRunningAnimations.remove(session); @@ -912,14 +912,14 @@ public class RippleDrawable extends LayerDrawable { ? mState.mColor.getColorForState(getState(), Color.BLACK) : mMaskColorFilter.getColor(); shader.setColor(color); - shader.setOrigin(x, y); + shader.setOrigin(w / 2, y / 2); + shader.setTouch(x, y); shader.setResolution(w, h); - shader.setSecondsOffset(0); + shader.setNoisePhase(0); shader.setRadius(radius); shader.setProgress(.0f); properties = new RippleAnimationSession.AnimationProperties<>( - x, y, radius, p, 0f, - shader); + w / 2, h / 2, radius, p, 0f, shader); if (mMaskShader == null) { shader.setShader(null); } else { diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java index 657a32c1ac46..ea9ba325a826 100644 --- a/graphics/java/android/graphics/drawable/RippleShader.java +++ b/graphics/java/android/graphics/drawable/RippleShader.java @@ -23,11 +23,12 @@ import android.graphics.Shader; final class RippleShader extends RuntimeShader { private static final String SHADER_UNIFORMS = "uniform vec2 in_origin;\n" + + "uniform vec2 in_touch;\n" + "uniform float in_progress;\n" + "uniform float in_maxRadius;\n" + "uniform vec2 in_resolution;\n" + "uniform float in_hasMask;\n" - + "uniform float in_secondsOffset;\n" + + "uniform float in_noisePhase;\n" + "uniform vec4 in_color;\n" + "uniform shader in_shader;\n"; private static final String SHADER_LIB = @@ -48,7 +49,7 @@ final class RippleShader extends RuntimeShader { + " float s = 0.0;\n" + " for (float i = 0; i < 4; i += 1) {\n" + " float l = i * 0.25;\n" - + " float h = l + 0.025;\n" + + " float h = l + 0.005;\n" + " float o = abs(sin(0.1 * PI * (t + i)));\n" + " s += threshold(n + o, l, h);\n" + " }\n" @@ -79,12 +80,12 @@ final class RippleShader extends RuntimeShader { + " float fadeIn = subProgress(0., 0.175, in_progress);\n" + " float fadeOutNoise = subProgress(0.375, 1., in_progress);\n" + " float fadeOutRipple = subProgress(0.375, 0.75, in_progress);\n" - + " float ring = getRingMask(p, in_origin, in_maxRadius, fadeIn);\n" + + " vec2 center = mix(in_touch, in_origin, fadeIn);\n" + + " float ring = getRingMask(p, center, in_maxRadius, fadeIn);\n" + " float alpha = min(fadeIn, 1. - fadeOutNoise);\n" - + " float sparkle = sparkles(p, in_progress * 0.25 + in_secondsOffset)\n" - + " * ring * alpha;\n" - + " float fade = min(fadeIn, 1.-fadeOutRipple);\n" - + " vec4 circle = in_color * (softCircle(p, in_origin, in_maxRadius " + + " float sparkle = sparkles(p, in_noisePhase) * ring * alpha;\n" + + " float fade = min(fadeIn, 1. - fadeOutRipple);\n" + + " vec4 circle = in_color * (softCircle(p, center, in_maxRadius " + " * fadeIn, 0.2) * fade);\n" + " float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n" + " return mix(circle, vec4(sparkle), sparkle) * mask;\n" @@ -109,14 +110,18 @@ final class RippleShader extends RuntimeShader { /** * Continuous offset used as noise phase. */ - public void setSecondsOffset(float t) { - setUniform("in_secondsOffset", t); + public void setNoisePhase(float t) { + setUniform("in_noisePhase", t); } public void setOrigin(float x, float y) { setUniform("in_origin", new float[] {x, y}); } + public void setTouch(float x, float y) { + setUniform("in_touch", new float[] {x, y}); + } + public void setProgress(float progress) { setUniform("in_progress", progress); } diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 3f03302de474..1b5dc8bdbcaa 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -38,14 +38,6 @@ filegroup { path: "src", } -filegroup { - name: "wm_shell-aidls", - srcs: [ - "src/**/*.aidl", - ], - path: "src", -} - // TODO(b/168581922) protologtool do not support kotlin(*.kt) filegroup { name: "wm_shell-sources-kt", @@ -106,7 +98,7 @@ android_library { ":wm_shell_protolog_src", // TODO(b/168581922) protologtool do not support kotlin(*.kt) ":wm_shell-sources-kt", - ":wm_shell-aidls", + "src/**/I*.aidl", ], resource_dirs: [ "res", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java index d451f4a0661b..eaed24d6195a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java @@ -47,7 +47,21 @@ public final class ShellCommandHandlerImpl { private final ShellExecutor mMainExecutor; private final HandlerImpl mImpl = new HandlerImpl(); - public ShellCommandHandlerImpl( + public static ShellCommandHandler create( + ShellTaskOrganizer shellTaskOrganizer, + Optional<LegacySplitScreenController> legacySplitScreenOptional, + Optional<SplitScreenController> splitScreenOptional, + Optional<Pip> pipOptional, + Optional<OneHandedController> oneHandedOptional, + Optional<HideDisplayCutoutController> hideDisplayCutout, + Optional<AppPairsController> appPairsOptional, + ShellExecutor mainExecutor) { + return new ShellCommandHandlerImpl(shellTaskOrganizer, legacySplitScreenOptional, + splitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout, + appPairsOptional, mainExecutor).mImpl; + } + + private ShellCommandHandlerImpl( ShellTaskOrganizer shellTaskOrganizer, Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, @@ -66,10 +80,6 @@ public final class ShellCommandHandlerImpl { mMainExecutor = mainExecutor; } - public ShellCommandHandler asShellCommandHandler() { - return mImpl; - } - /** Dumps WM Shell internal state. */ private void dump(PrintWriter pw) { mShellTaskOrganizer.dump(pw, ""); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java index 6f4550c2a89e..85bd24c1c2bf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -26,7 +26,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.splitscreen.SplitScreenController; -import com.android.wm.shell.startingsurface.StartingWindowController; +import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -47,20 +47,44 @@ public class ShellInitImpl { private final FullscreenTaskListener mFullscreenTaskListener; private final ShellExecutor mMainExecutor; private final Transitions mTransitions; - private final StartingWindowController mStartingWindow; + private final Optional<StartingSurface> mStartingSurfaceOptional; private final InitImpl mImpl = new InitImpl(); - public ShellInitImpl(DisplayImeController displayImeController, + public static ShellInit create(DisplayImeController displayImeController, DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<AppPairsController> appPairsOptional, + Optional<StartingSurface> startingSurfaceOptional, + Optional<PipTouchHandler> pipTouchHandlerOptional, + FullscreenTaskListener fullscreenTaskListener, + Transitions transitions, + ShellExecutor mainExecutor) { + return new ShellInitImpl(displayImeController, + dragAndDropController, + shellTaskOrganizer, + legacySplitScreenOptional, + splitScreenOptional, + appPairsOptional, + startingSurfaceOptional, + pipTouchHandlerOptional, + fullscreenTaskListener, + transitions, + mainExecutor).mImpl; + } + + private ShellInitImpl(DisplayImeController displayImeController, + DragAndDropController dragAndDropController, + ShellTaskOrganizer shellTaskOrganizer, + Optional<LegacySplitScreenController> legacySplitScreenOptional, + Optional<SplitScreenController> splitScreenOptional, + Optional<AppPairsController> appPairsOptional, + Optional<StartingSurface> startingSurfaceOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, - StartingWindowController startingWindow, ShellExecutor mainExecutor) { mDisplayImeController = displayImeController; mDragAndDropController = dragAndDropController; @@ -72,21 +96,17 @@ public class ShellInitImpl { mPipTouchHandlerOptional = pipTouchHandlerOptional; mTransitions = transitions; mMainExecutor = mainExecutor; - mStartingWindow = startingWindow; - } - - public ShellInit asShellInit() { - return mImpl; + mStartingSurfaceOptional = startingSurfaceOptional; } private void init() { // Start listening for display changes mDisplayImeController.startMonitorDisplays(); - // Setup the shell organizer mShellTaskOrganizer.addListenerForType( mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN); - mShellTaskOrganizer.initStartingWindow(mStartingWindow); + mStartingSurfaceOptional.ifPresent(mShellTaskOrganizer::initStartingSurface); + // Register the shell organizer mShellTaskOrganizer.registerOrganizer(); mAppPairsOptional.ifPresent(AppPairsController::onOrganizerRegistered); 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 94d13eab4299..fcb53cd890b7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -48,7 +48,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.sizecompatui.SizeCompatUIController; -import com.android.wm.shell.startingsurface.StartingWindowController; +import com.android.wm.shell.startingsurface.StartingSurface; import java.io.PrintWriter; import java.util.ArrayList; @@ -133,7 +133,7 @@ public class ShellTaskOrganizer extends TaskOrganizer { private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>(); private final Object mLock = new Object(); - private StartingWindowController mStartingWindow; + private StartingSurface mStartingSurface; /** * In charge of showing size compat UI. Can be {@code null} if device doesn't support size @@ -184,8 +184,8 @@ public class ShellTaskOrganizer extends TaskOrganizer { /** * @hide */ - public void initStartingWindow(StartingWindowController startingWindow) { - mStartingWindow = startingWindow; + public void initStartingSurface(StartingSurface startingSurface) { + mStartingSurface = startingSurface; } /** @@ -302,23 +302,23 @@ public class ShellTaskOrganizer extends TaskOrganizer { @Override public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { - if (mStartingWindow != null) { - mStartingWindow.addStartingWindow(info, appToken); + if (mStartingSurface != null) { + mStartingSurface.addStartingWindow(info, appToken); } } @Override public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, boolean playRevealAnimation) { - if (mStartingWindow != null) { - mStartingWindow.removeStartingWindow(taskId, leash, frame, playRevealAnimation); + if (mStartingSurface != null) { + mStartingSurface.removeStartingWindow(taskId, leash, frame, playRevealAnimation); } } @Override public void copySplashScreenView(int taskId) { - if (mStartingWindow != null) { - mStartingWindow.copySplashScreenView(taskId); + if (mStartingSurface != null) { + mStartingSurface.copySplashScreenView(taskId); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java deleted file mode 100644 index b29058b1f204..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 android.Manifest; -import android.util.Slog; - -import java.util.function.Consumer; - -/** - * Helpers for working with executors - */ -public class ExecutorUtils { - - /** - * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given - * callback. - */ - public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance, - String log, Consumer<T> callback) { - executeRemoteCallWithTaskPermission(controllerInstance, log, callback, - false /* blocking */); - } - - /** - * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given - * callback. - */ - public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance, - String log, Consumer<T> callback, boolean blocking) { - if (controllerInstance == null) return; - - final RemoteCallable<T> controller = controllerInstance; - controllerInstance.getContext().enforceCallingPermission( - Manifest.permission.MANAGE_ACTIVITY_TASKS, log); - if (blocking) { - try { - controllerInstance.getRemoteCallExecutor().executeBlocking(() -> { - callback.accept((T) controller); - }); - } catch (InterruptedException e) { - Slog.e("ExecutorUtils", "Remote call failed", e); - } - } else { - controllerInstance.getRemoteCallExecutor().execute(() -> { - callback.accept((T) controller); - }); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index 4bb8e9b6581f..9dabec7a13d0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -32,7 +32,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DragEvent; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindow; import android.view.IWindowManager; import android.view.IWindowSession; @@ -369,9 +369,9 @@ public class SystemWindows { public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {} @Override - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { + public void requestScrollCapture(IScrollCaptureResponseListener listener) { try { - callbacks.onScrollCaptureResponse( + listener.onScrollCaptureResponse( new ScrollCaptureResponse.Builder() .setDescription("Not Implemented") .build()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index 17709438baba..58bf22ad29b2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -85,7 +85,8 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange @Override public void onDisplayAdded(int displayId) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display added: %d", displayId); - final Context context = mDisplayController.getDisplayContext(displayId); + final Context context = mDisplayController.getDisplayContext(displayId) + .createWindowContext(TYPE_APPLICATION_OVERLAY, null); final WindowManager wm = context.getSystemService(WindowManager.class); // TODO(b/169894807): Figure out the right layer for this, needs to be below the task bar diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 9a09ca43d1d7..aab2334f8255 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -235,7 +235,7 @@ public class DragAndDropPolicy { mStarter.startShortcut(packageName, id, stage, position, opts, user); } else { mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), - null, stage, position, opts); + mContext, null, stage, position, opts); } } @@ -295,7 +295,7 @@ public class DragAndDropPolicy { @Nullable Bundle options); void startShortcut(String packageName, String shortcutId, @StageType int stage, @StagePosition int position, @Nullable Bundle options, UserHandle user); - void startIntent(PendingIntent intent, Intent fillInIntent, + void startIntent(PendingIntent intent, Context context, Intent fillInIntent, @StageType int stage, @StagePosition int position, @Nullable Bundle options); void enterSplitScreen(int taskId, boolean leftOrTop); @@ -337,8 +337,9 @@ public class DragAndDropPolicy { } @Override - public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, int stage, - int position, @Nullable Bundle options) { + public void startIntent(PendingIntent intent, Context context, + @Nullable Intent fillInIntent, int stage, int position, + @Nullable Bundle options) { try { intent.send(mContext, 0, fillInIntent, null, null, null, options); } catch (PendingIntent.CanceledException e) { 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 a7e9a0135de0..4f31c370108d 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 @@ -26,14 +26,6 @@ import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEv */ @ExternalThread public interface OneHanded { - - /** - * Returns a binder that can be passed to an external process to manipulate OneHanded. - */ - default IOneHanded createExternalInterface() { - return null; - } - /** * Return one handed settings enabled or not. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 8022c1b6e047..44dad57ea3b8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -18,10 +18,6 @@ package com.android.wm.shell.onehanded; import static android.os.UserHandle.USER_CURRENT; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; - -import android.Manifest; -import android.annotation.BinderThread; import android.content.ComponentName; import android.content.Context; import android.content.om.IOverlayManager; @@ -47,8 +43,6 @@ import com.android.internal.logging.UiEventLogger; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.ExecutorUtils; -import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; @@ -60,7 +54,7 @@ import java.io.PrintWriter; /** * Manages and manipulates the one handed states, transitions, and gesture for phones. */ -public class OneHandedController implements RemoteCallable<OneHandedController> { +public class OneHandedController { private static final String TAG = "OneHandedController"; private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = @@ -170,7 +164,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> new OneHandedBackgroundPanelOrganizer(context, windowManager, displayController, mainExecutor); OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer( - context, windowManager, displayController, animationController, tutorialHandler, + context, windowManager, animationController, tutorialHandler, oneHandedBackgroundPanelOrganizer, mainExecutor); OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger); IOverlayManager overlayManager = IOverlayManager.Stub.asInterface( @@ -245,16 +239,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController> return mImpl; } - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - /** * Set one handed enabled or disabled when user update settings */ @@ -583,22 +567,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } } - /** - * The interface for calls from outside the Shell, within the host process. - */ @ExternalThread private class OneHandedImpl implements OneHanded { - private IOneHandedImpl mIOneHanded; - - @Override - public IOneHanded createExternalInterface() { - if (mIOneHanded != null) { - mIOneHanded.invalidate(); - } - mIOneHanded = new IOneHandedImpl(OneHandedController.this); - return mIOneHanded; - } - @Override public boolean isOneHandedEnabled() { // This is volatile so return directly @@ -667,39 +637,4 @@ public class OneHandedController implements RemoteCallable<OneHandedController> }); } } - - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class IOneHandedImpl extends IOneHanded.Stub { - private OneHandedController mController; - - IOneHandedImpl(OneHandedController controller) { - mController = controller; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mController = null; - } - - @Override - public void startOneHanded() { - executeRemoteCallWithTaskPermission(mController, "startOneHanded", - (controller) -> { - controller.startOneHanded(); - }); - } - - @Override - public void stopOneHanded() { - executeRemoteCallWithTaskPermission(mController, "stopOneHanded", - (controller) -> { - controller.stopOneHanded(); - }); - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index 0238fa8a7936..5eec23106f47 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -37,7 +37,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.wm.shell.R; -import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; import java.io.PrintWriter; @@ -67,7 +66,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private int mEnterExitAnimationDurationMs; private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap(); - private DisplayController mDisplayController; private OneHandedAnimationController mAnimationController; private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; @@ -111,7 +109,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { */ public OneHandedDisplayAreaOrganizer(Context context, WindowManager windowManager, - DisplayController displayController, OneHandedAnimationController animationController, OneHandedTutorialHandler tutorialHandler, OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer, @@ -119,7 +116,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { super(mainExecutor); mWindowManager = windowManager; mAnimationController = animationController; - mDisplayController = displayController; mLastVisualDisplayBounds.set(getDisplayBounds()); final int animationDurationConfig = context.getResources().getInteger( R.integer.config_one_handed_translate_animation_duration); @@ -171,10 +167,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { */ public void onRotateDisplay(int fromRotation, int toRotation, WindowContainerTransaction wct) { // Stop one handed without animation and reset cropped size immediately - final Rect newBounds = new Rect(mDefaultDisplayBounds); + final Rect newBounds = new Rect(getDisplayBounds()); + // This diff rule will only filter the cases portrait <-> landscape final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1; if (isOrientationDiff) { + // getDisplayBounds() will return window metrics bounds which dose not update to + // corresponding display orientation yet, we have to manual rotate bounds + newBounds.set(0, 0, newBounds.bottom, newBounds.right); resetWindowsOffset(wct); mDefaultDisplayBounds.set(newBounds); mLastVisualDisplayBounds.set(newBounds); @@ -293,7 +293,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { } @Nullable - private Rect getDisplayBounds() { + @VisibleForTesting + Rect getDisplayBounds() { if (mWindowManager == null) { Slog.e(TAG, "WindowManager instance is null! Can not get display size!"); return new Rect(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl deleted file mode 100644 index a6ffa6e44584..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 android.app.PictureInPictureParams; -import android.content.ComponentName; -import android.content.pm.ActivityInfo; -import android.graphics.Rect; - -import com.android.wm.shell.pip.IPipAnimationListener; - -/** - * Interface that is exposed to remote callers to manipulate the Pip feature. - */ -interface IPip { - - /** - * Notifies that Activity is about to be swiped to home with entering PiP transition and - * queries the destination bounds for PiP depends on Launcher's rotation and shelf height. - * - * @param componentName ComponentName represents the Activity - * @param activityInfo ActivityInfo tied to the Activity - * @param pictureInPictureParams PictureInPictureParams tied to the Activity - * @param launcherRotation Launcher rotation to calculate the PiP destination bounds - * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds - * @return destination bounds the PiP window should land into - */ - Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo, - in PictureInPictureParams pictureInPictureParams, - int launcherRotation, int shelfHeight) = 1; - - /** - * Notifies the swiping Activity to PiP onto home transition is finished - * - * @param componentName ComponentName represents the Activity - * @param destinationBounds the destination bounds the PiP window lands into - */ - oneway void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 2; - - /** - * Sets listener to get pinned stack animation callbacks. - */ - oneway void setPinnedStackAnimationListener(IPipAnimationListener listener) = 3; - - /** - * Sets the shelf height and visibility. - */ - oneway void setShelfHeight(boolean visible, int shelfHeight) = 4; -} 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 6d4773bdeb1f..d14c3e3c0dd4 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 @@ -16,10 +16,15 @@ package com.android.wm.shell.pip; +import android.annotation.Nullable; +import android.app.PictureInPictureParams; +import android.content.ComponentName; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.pip.phone.PipTouchHandler; import java.io.PrintWriter; import java.util.function.Consumer; @@ -29,14 +34,6 @@ import java.util.function.Consumer; */ @ExternalThread public interface Pip { - - /** - * Returns a binder that can be passed to an external process to manipulate PIP. - */ - default IPip createExternalInterface() { - return null; - } - /** * Expand PIP, it's possible that specific request to activate the window via Alt-tab. */ @@ -112,6 +109,30 @@ public interface Pip { default void showPictureInPictureMenu() {} /** + * Called by Launcher when swiping an auto-pip enabled Activity to home starts + * @param componentName {@link ComponentName} represents the Activity entering PiP + * @param activityInfo {@link ActivityInfo} tied to the Activity + * @param pictureInPictureParams {@link PictureInPictureParams} tied to the Activity + * @param launcherRotation Rotation Launcher is in + * @param shelfHeight Shelf height when landing PiP window onto Launcher + * @return Destination bounds of PiP window based on the parameters passed in + */ + default Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) { + return null; + } + + /** + * Called by Launcher when swiping an auto-pip enable Activity to home finishes + * @param componentName {@link ComponentName} represents the Activity entering PiP + * @param destinationBounds Destination bounds of PiP window + */ + default void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + return; + } + + /** * Called by NavigationBar in order to listen in for PiP bounds change. This is mostly used * for times where the PiP bounds could conflict with SystemUI elements, such as a stashed * PiP and the Back-from-Edge gesture. 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 9ec7c0d173dd..36dc4e409f98 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 @@ -41,6 +41,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.PictureInPictureParams; @@ -423,12 +424,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (mInSwipePipToHomeTransition) { final Rect destinationBounds = mPipBoundsState.getBounds(); + final SurfaceControl.Transaction tx = + mSurfaceControlTransactionFactory.getTransaction(); + mSurfaceTransactionHelper.resetScale(tx, mLeash, destinationBounds); + mSurfaceTransactionHelper.crop(tx, mLeash, destinationBounds); // animation is finished in the Launcher and here we directly apply the final touch. applyEnterPipSyncTransaction(destinationBounds, () -> { // ensure menu's settled in its final bounds first finishResizeForMenu(destinationBounds); sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); - }); + }, tx); mInSwipePipToHomeTransition = false; return; } @@ -490,16 +495,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // mState is set right after the animation is kicked off to block any resize // requests such as offsetPip that may have been called prior to the transition. mState = State.ENTERING_PIP; - }); + }, null /* boundsChangeTransaction */); } - private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable) { + private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable, + @Nullable SurfaceControl.Transaction boundsChangeTransaction) { // PiP menu is attached late in the process here to avoid any artifacts on the leash // caused by addShellRoot when in gesture navigation mode. mPipMenuController.attach(mLeash); final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setBounds(mToken, destinationBounds); + if (boundsChangeTransaction != null) { + wct.setBoundsChangeTransaction(mToken, boundsChangeTransaction); + } wct.scheduleFinishEnterPip(mToken, destinationBounds); mSyncTransactionQueue.queue(wct); if (runnable != null) { 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 501b90ea6828..9a584c67f97c 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 @@ -21,10 +21,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static android.view.WindowManager.INPUT_CONSUMER_PIP; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; -import android.Manifest; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.PictureInPictureParams; @@ -35,7 +33,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ParceledListSlice; import android.content.res.Configuration; import android.graphics.Rect; -import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -47,7 +44,6 @@ import android.view.DisplayInfo; import android.view.WindowManagerGlobal; import android.window.WindowContainerTransaction; -import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -55,13 +51,9 @@ 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.ExecutorUtils; -import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.pip.IPip; -import com.android.wm.shell.pip.IPipAnimationListener; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsAlgorithm; @@ -79,8 +71,7 @@ import java.util.function.Consumer; /** * Manages the picture-in-picture (PIP) UI and states for Phones. */ -public class PipController implements PipTransitionController.PipTransitionCallback, - RemoteCallable<PipController> { +public class PipController implements PipTransitionController.PipTransitionCallback { private static final String TAG = "PipController"; private Context mContext; @@ -94,12 +85,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb private PipBoundsState mPipBoundsState; private PipTouchHandler mTouchHandler; private PipTransitionController mPipTransitionController; - protected final PipImpl mImpl; + protected final PipImpl mImpl = new PipImpl(); private final Rect mTmpInsetBounds = new Rect(); private boolean mIsInFixedRotation; - private IPipAnimationListener mPinnedStackAnimationRecentsCallback; + private Consumer<Boolean> mPinnedStackAnimationRecentsCallback; protected PhonePipMenuController mMenuController; protected PipTaskOrganizer mPipTaskOrganizer; @@ -273,7 +264,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb } mContext = context; - mImpl = new PipImpl(); mWindowManagerShellWrapper = windowManagerShellWrapper; mDisplayController = displayController; mPipBoundsAlgorithm = pipBoundsAlgorithm; @@ -376,16 +366,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb }); } - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - private void onConfigurationChanged(Configuration newConfig) { mPipBoundsAlgorithm.onConfigurationChanged(mContext); mTouchHandler.onConfigurationChanged(); @@ -494,7 +474,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipTaskOrganizer.setOneShotAnimationType(animationType); } - private void setPinnedStackAnimationListener(IPipAnimationListener callback) { + private void setPinnedStackAnimationListener(Consumer<Boolean> callback) { mPinnedStackAnimationRecentsCallback = callback; } @@ -532,11 +512,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb // Disable touches while the animation is running mTouchHandler.setTouchEnabled(false); if (mPinnedStackAnimationRecentsCallback != null) { - try { - mPinnedStackAnimationRecentsCallback.onPipAnimationStarted(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to call onPinnedStackAnimationStarted()", e); - } + mPinnedStackAnimationRecentsCallback.accept(true); } } @@ -662,21 +638,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipInputConsumer.dump(pw, innerPrefix); } - /** - * The interface for calls from outside the Shell, within the host process. - */ private class PipImpl implements Pip { - private IPipImpl mIPip; - - @Override - public IPip createExternalInterface() { - if (mIPip != null) { - mIPip.invalidate(); - } - mIPip = new IPipImpl(PipController.this); - return mIPip; - } - @Override public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) { mMainExecutor.execute(() -> { @@ -734,6 +696,13 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override + public void setPinnedStackAnimationListener(Consumer<Boolean> callback) { + mMainExecutor.execute(() -> { + PipController.this.setPinnedStackAnimationListener(callback); + }); + } + + @Override public void setPinnedStackAnimationType(int animationType) { mMainExecutor.execute(() -> { PipController.this.setPinnedStackAnimationType(animationType); @@ -755,99 +724,37 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override - public void dump(PrintWriter pw) { + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, int launcherRotation, + int shelfHeight) { + Rect[] result = new Rect[1]; try { mMainExecutor.executeBlocking(() -> { - PipController.this.dump(pw); + result[0] = PipController.this.startSwipePipToHome(componentName, activityInfo, + pictureInPictureParams, launcherRotation, shelfHeight); }); } catch (InterruptedException e) { - Slog.e(TAG, "Failed to dump PipController in 2s"); + Slog.e(TAG, "Failed to start swipe pip to home"); } - } - } - - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class IPipImpl extends IPip.Stub { - private PipController mController; - private IPipAnimationListener mListener; - private final IBinder.DeathRecipient mListenerDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - final PipController controller = mController; - controller.getRemoteCallExecutor().execute(() -> { - mListener = null; - controller.setPinnedStackAnimationListener(null); - }); - } - }; - - IPipImpl(PipController controller) { - mController = controller; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mController = null; - } - - @Override - public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, - PictureInPictureParams pictureInPictureParams, int launcherRotation, - int shelfHeight) { - Rect[] result = new Rect[1]; - executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome", - (controller) -> { - result[0] = controller.startSwipePipToHome(componentName, activityInfo, - pictureInPictureParams, launcherRotation, shelfHeight); - }, true /* blocking */); return result[0]; } @Override public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { - executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome", - (controller) -> { - controller.stopSwipePipToHome(componentName, destinationBounds); - }); - } - - @Override - public void setShelfHeight(boolean visible, int height) { - executeRemoteCallWithTaskPermission(mController, "setShelfHeight", - (controller) -> { - controller.setShelfHeight(visible, height); - }); + mMainExecutor.execute(() -> { + PipController.this.stopSwipePipToHome(componentName, destinationBounds); + }); } @Override - public void setPinnedStackAnimationListener(IPipAnimationListener listener) { - executeRemoteCallWithTaskPermission(mController, "setPinnedStackAnimationListener", - (controller) -> { - if (mListener != null) { - // Reset the old death recipient - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - if (listener != null) { - // Register the death recipient for the new listener to clear the listener - try { - listener.asBinder().linkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death"); - return; - } - } - mListener = listener; - controller.setPinnedStackAnimationListener(listener); - }); + public void dump(PrintWriter pw) { + try { + mMainExecutor.executeBlocking(() -> { + PipController.this.dump(pw); + }); + } catch (InterruptedException e) { + Slog.e(TAG, "Failed to dump PipController in 2s"); + } } } } 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 543ecfcf1a33..44e262492b97 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 @@ -73,6 +73,7 @@ public class PipTouchHandler { // Allow PIP to resize to a slightly bigger state upon touch private boolean mEnableResize; private final Context mContext; + private final PipTaskOrganizer mPipTaskOrganizer; private final PipBoundsAlgorithm mPipBoundsAlgorithm; private final @NonNull PipBoundsState mPipBoundsState; private final PipUiEventLogger mPipUiEventLogger; @@ -169,6 +170,7 @@ public class PipTouchHandler { mContext = context; mMainExecutor = mainExecutor; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); + mPipTaskOrganizer = pipTaskOrganizer; mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipBoundsState = pipBoundsState; mMenuController = menuController; @@ -982,7 +984,9 @@ public class PipTouchHandler { void setPipExclusionBoundsChangeListener(Consumer<Rect> pipExclusionBoundsChangeListener) { mPipExclusionBoundsChangeListener = new WeakReference<>(pipExclusionBoundsChangeListener); - pipExclusionBoundsChangeListener.accept(mPipBoundsState.getBounds()); + pipExclusionBoundsChangeListener.accept(mPipTaskOrganizer.isInPip() + ? mPipBoundsState.getBounds() : new Rect()); + } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl deleted file mode 100644 index 0c46eaba18ae..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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.splitscreen; - -import android.app.PendingIntent; -import android.content.Intent; -import android.os.Bundle; -import android.os.UserHandle; - -import com.android.wm.shell.splitscreen.ISplitScreenListener; - -/** - * Interface that is exposed to remote callers to manipulate the splitscreen feature. - */ -interface ISplitScreen { - - /** - * Registers a split screen listener. - */ - oneway void registerSplitScreenListener(in ISplitScreenListener listener) = 1; - - /** - * Unregisters a split screen listener. - */ - oneway void unregisterSplitScreenListener(in ISplitScreenListener listener) = 2; - - /** - * Hides the side-stage if it is currently visible. - */ - oneway void setSideStageVisibility(boolean visible) = 3; - - /** - * Removes a task from the side stage. - */ - oneway void removeFromSideStage(int taskId) = 4; - - /** - * Removes the split-screen stages. - */ - oneway void exitSplitScreen() = 5; - - /** - * @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. - */ - oneway void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 6; - - /** - * Starts a task in a stage. - */ - oneway void startTask(int taskId, int stage, int position, in Bundle options) = 7; - - /** - * Starts a shortcut in a stage. - */ - oneway void startShortcut(String packageName, String shortcutId, int stage, int position, - in Bundle options, in UserHandle user) = 8; - - /** - * Starts an activity in a stage. - */ - oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int stage, - int position, in Bundle options) = 9; -} 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 340b55d7f446..25a84bd46484 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 @@ -35,7 +35,7 @@ import com.android.wm.shell.draganddrop.DragAndDropPolicy; * TODO: Figure out which of these are actually needed outside of the Shell */ @ExternalThread -public interface SplitScreen { +public interface SplitScreen extends DragAndDropPolicy.Starter { /** * Stage position isn't specified normally meaning to use what ever it is currently set to. */ @@ -89,10 +89,35 @@ public interface SplitScreen { void onTaskStageChanged(int taskId, @StageType int stage, boolean visible); } - /** - * Returns a binder that can be passed to an external process to manipulate SplitScreen. - */ - default ISplitScreen createExternalInterface() { - return null; - } + /** @return {@code true} if split-screen is currently visible. */ + boolean isSplitScreenVisible(); + /** Moves a task in the side-stage of split-screen. */ + boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition); + /** Moves a task in the side-stage of split-screen. */ + boolean moveToSideStage(ActivityManager.RunningTaskInfo task, + @StagePosition int sideStagePosition); + /** Removes a task from the side-stage of split-screen. */ + boolean removeFromSideStage(int taskId); + /** Sets the position of the side-stage. */ + void setSideStagePosition(@StagePosition int sideStagePosition); + /** Hides the side-stage if it is currently visible. */ + void setSideStageVisibility(boolean visible); + + /** Removes the split-screen stages. */ + void exitSplitScreen(); + /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */ + void exitSplitScreenOnHide(boolean exitSplitScreenOnHide); + /** Gets the stage bounds. */ + void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds); + + void registerSplitScreenListener(SplitScreenListener listener); + void unregisterSplitScreenListener(SplitScreenListener listener); + + void startTask(int taskId, + @StageType int stage, @StagePosition int position, @Nullable Bundle options); + void startShortcut(String packageName, String shortcutId, @StageType int stage, + @StagePosition int position, @Nullable Bundle options, UserHandle user); + void startIntent(PendingIntent intent, Context context, + @Nullable Intent fillInIntent, @StageType int stage, + @StagePosition int position, @Nullable Bundle options); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index d4362efe462d..bb6f6f259a1e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -18,7 +18,6 @@ package com.android.wm.shell.splitscreen; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED; @@ -35,24 +34,19 @@ import android.content.Intent; import android.content.pm.LauncherApps; import android.graphics.Rect; import android.os.Bundle; -import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; -import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropPolicy; -import com.android.wm.shell.splitscreen.ISplitScreenListener; import java.io.PrintWriter; @@ -61,8 +55,7 @@ import java.io.PrintWriter; * {@link SplitScreen}. * @see StageCoordinator */ -public class SplitScreenController implements DragAndDropPolicy.Starter, - RemoteCallable<SplitScreenController> { +public class SplitScreenController implements DragAndDropPolicy.Starter { private static final String TAG = SplitScreenController.class.getSimpleName(); private final ShellTaskOrganizer mTaskOrganizer; @@ -91,16 +84,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mImpl; } - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - public void onOrganizerRegistered() { if (mStageCoordinator == null) { // TODO: Multi-display @@ -189,13 +172,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } - public void startIntent(PendingIntent intent, Intent fillInIntent, - @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position, - @Nullable Bundle options) { + public void startIntent(PendingIntent intent, Context context, + Intent fillInIntent, @SplitScreen.StageType int stage, + @SplitScreen.StagePosition int position, @Nullable Bundle options) { options = resolveStartStage(stage, position, options); try { - intent.send(mContext, 0, fillInIntent, null, null, null, options); + intent.send(context, 0, fillInIntent, null, null, null, options); } catch (PendingIntent.CanceledException e) { Slog.e(TAG, "Failed to launch activity", e); } @@ -259,170 +242,121 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } - /** - * The interface for calls from outside the Shell, within the host process. - */ - @ExternalThread private class SplitScreenImpl implements SplitScreen { - private ISplitScreenImpl mISplitScreen; - @Override - public ISplitScreen createExternalInterface() { - if (mISplitScreen != null) { - mISplitScreen.invalidate(); - } - mISplitScreen = new ISplitScreenImpl(SplitScreenController.this); - return mISplitScreen; + public boolean isSplitScreenVisible() { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.isSplitScreenVisible(); + }, Boolean.class); } - } - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class ISplitScreenImpl extends ISplitScreen.Stub { - private SplitScreenController mController; - private ISplitScreenListener mListener; - private final SplitScreen.SplitScreenListener mSplitScreenListener = - new SplitScreen.SplitScreenListener() { - @Override - public void onStagePositionChanged(int stage, int position) { - try { - if (mListener != null) { - mListener.onStagePositionChanged(stage, position); - } - } catch (RemoteException e) { - Slog.e(TAG, "onStagePositionChanged", e); - } - } + @Override + public boolean moveToSideStage(int taskId, int sideStagePosition) { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.moveToSideStage(taskId, sideStagePosition); + }, Boolean.class); + } - @Override - public void onTaskStageChanged(int taskId, int stage, boolean visible) { - try { - if (mListener != null) { - mListener.onTaskStageChanged(taskId, stage, visible); - } - } catch (RemoteException e) { - Slog.e(TAG, "onTaskStageChanged", e); - } - } - }; - private final IBinder.DeathRecipient mListenerDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - final SplitScreenController controller = mController; - controller.getRemoteCallExecutor().execute(() -> { - mListener = null; - controller.unregisterSplitScreenListener(mSplitScreenListener); - }); - } - }; + @Override + public boolean moveToSideStage(ActivityManager.RunningTaskInfo task, + int sideStagePosition) { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.moveToSideStage(task, sideStagePosition); + }, Boolean.class); + } - public ISplitScreenImpl(SplitScreenController controller) { - mController = controller; + @Override + public boolean removeFromSideStage(int taskId) { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.removeFromSideStage(taskId); + }, Boolean.class); } - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mController = null; + @Override + public void setSideStagePosition(int sideStagePosition) { + mMainExecutor.execute(() -> { + SplitScreenController.this.setSideStagePosition(sideStagePosition); + }); } @Override - public void registerSplitScreenListener(ISplitScreenListener listener) { - executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener", - (controller) -> { - if (mListener != null) { - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - if (listener != null) { - try { - listener.asBinder().linkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death"); - return; - } - } - mListener = listener; - controller.registerSplitScreenListener(mSplitScreenListener); - }); + public void setSideStageVisibility(boolean visible) { + mMainExecutor.execute(() -> { + SplitScreenController.this.setSideStageVisibility(visible); + }); } @Override - public void unregisterSplitScreenListener(ISplitScreenListener listener) { - executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener", - (controller) -> { - if (mListener != null) { - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - mListener = null; - controller.unregisterSplitScreenListener(mSplitScreenListener); - }); + public void enterSplitScreen(int taskId, boolean leftOrTop) { + mMainExecutor.execute(() -> { + SplitScreenController.this.enterSplitScreen(taskId, leftOrTop); + }); } @Override public void exitSplitScreen() { - executeRemoteCallWithTaskPermission(mController, "exitSplitScreen", - (controller) -> { - controller.exitSplitScreen(); - }); + mMainExecutor.execute(() -> { + SplitScreenController.this.exitSplitScreen(); + }); } @Override public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { - executeRemoteCallWithTaskPermission(mController, "exitSplitScreenOnHide", - (controller) -> { - controller.exitSplitScreenOnHide(exitSplitScreenOnHide); - }); + mMainExecutor.execute(() -> { + SplitScreenController.this.exitSplitScreenOnHide(exitSplitScreenOnHide); + }); } @Override - public void setSideStageVisibility(boolean visible) { - executeRemoteCallWithTaskPermission(mController, "setSideStageVisibility", - (controller) -> { - controller.setSideStageVisibility(visible); - }); + public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { + try { + mMainExecutor.executeBlocking(() -> { + SplitScreenController.this.getStageBounds(outTopOrLeftBounds, + outBottomOrRightBounds); + }); + } catch (InterruptedException e) { + Slog.e(TAG, "Failed to get stage bounds in 2s"); + } + } + + @Override + public void registerSplitScreenListener(SplitScreenListener listener) { + mMainExecutor.execute(() -> { + SplitScreenController.this.registerSplitScreenListener(listener); + }); } @Override - public void removeFromSideStage(int taskId) { - executeRemoteCallWithTaskPermission(mController, "removeFromSideStage", - (controller) -> { - controller.removeFromSideStage(taskId); - }); + public void unregisterSplitScreenListener(SplitScreenListener listener) { + mMainExecutor.execute(() -> { + SplitScreenController.this.unregisterSplitScreenListener(listener); + }); } @Override public void startTask(int taskId, int stage, int position, @Nullable Bundle options) { - executeRemoteCallWithTaskPermission(mController, "startTask", - (controller) -> { - controller.startTask(taskId, stage, position, options); - }); + mMainExecutor.execute(() -> { + SplitScreenController.this.startTask(taskId, stage, position, options); + }); } @Override public void startShortcut(String packageName, String shortcutId, int stage, int position, @Nullable Bundle options, UserHandle user) { - executeRemoteCallWithTaskPermission(mController, "startShortcut", - (controller) -> { - controller.startShortcut(packageName, shortcutId, stage, position, - options, user); - }); + mMainExecutor.execute(() -> { + SplitScreenController.this.startShortcut(packageName, shortcutId, stage, position, + options, user); + }); } @Override - public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position, - @Nullable Bundle options) { - executeRemoteCallWithTaskPermission(mController, "startIntent", - (controller) -> { - controller.startIntent(intent, fillInIntent, stage, position, options); - }); + public void startIntent(PendingIntent intent, Context context, Intent fillInIntent, + int stage, int position, @Nullable Bundle options) { + mMainExecutor.execute(() -> { + SplitScreenController.this.startIntent(intent, context, fillInIntent, stage, + position, options); + }); } } + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java index 079d68973852..f258286f2d17 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java @@ -16,15 +16,35 @@ package com.android.wm.shell.startingsurface; +import android.graphics.Rect; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.window.StartingWindowInfo; + +import java.util.function.BiConsumer; /** * Interface to engage starting window feature. */ public interface StartingSurface { + /** + * Called when a task need a starting window. + */ + void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken); + /** + * Called when the content of a task is ready to show, starting window can be removed. + */ + void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation); + /** + * Called when the Task wants to copy the splash screen. + * @param taskId + */ + void copySplashScreenView(int taskId); /** - * Returns a binder that can be passed to an external process to manipulate starting windows. + * Registers the starting window listener. + * + * @param listener The callback when need a starting window. */ - default IStartingWindow createExternalInterface() { - return null; - } + void setStartingWindowListener(BiConsumer<Integer, Integer> listener); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index b6ca86989690..a694e525a761 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -25,8 +25,6 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; - import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.Context; @@ -39,9 +37,6 @@ import android.window.StartingWindowInfo; import android.window.TaskOrganizer; import android.window.TaskSnapshot; -import androidx.annotation.BinderThread; - -import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; @@ -63,7 +58,7 @@ import java.util.function.BiConsumer; * constructor to keep everything synchronized. * @hide */ -public class StartingWindowController implements RemoteCallable<StartingWindowController> { +public class StartingWindowController { private static final String TAG = StartingWindowController.class.getSimpleName(); static final boolean DEBUG_SPLASH_SCREEN = false; static final boolean DEBUG_TASK_SNAPSHOT = false; @@ -73,17 +68,17 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo private BiConsumer<Integer, Integer> mTaskLaunchingCallback; private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl(); - private final Context mContext; private final ShellExecutor mSplashScreenExecutor; // For Car Launcher public StartingWindowController(Context context, ShellExecutor splashScreenExecutor) { - this(context, splashScreenExecutor, new TransactionPool()); + mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, + new TransactionPool()); + mSplashScreenExecutor = splashScreenExecutor; } public StartingWindowController(Context context, ShellExecutor splashScreenExecutor, TransactionPool pool) { - mContext = context; mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, pool); mSplashScreenExecutor = splashScreenExecutor; } @@ -95,16 +90,6 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo return mImpl; } - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mSplashScreenExecutor; - } - private static class StartingTypeChecker { TaskSnapshot mSnapshot; @@ -203,121 +188,59 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo /** * Called when a task need a starting window. */ - public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { - mSplashScreenExecutor.execute(() -> { - final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo); - final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo; - if (mTaskLaunchingCallback != null) { - mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType); - } - if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) { - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken); - } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { - final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot; - mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot); - } - // If prefer don't show, then don't show! - }); + void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { + final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo); + final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo; + if (mTaskLaunchingCallback != null) { + mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType); + } + if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) { + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken); + } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { + final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot; + mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot); + } + // If prefer don't show, then don't show! } - public void copySplashScreenView(int taskId) { - mSplashScreenExecutor.execute(() -> { - mStartingSurfaceDrawer.copySplashScreenView(taskId); - }); + void copySplashScreenView(int taskId) { + mStartingSurfaceDrawer.copySplashScreenView(taskId); } /** * Called when the content of a task is ready to show, starting window can be removed. */ - public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, boolean playRevealAnimation) { - mSplashScreenExecutor.execute(() -> { - mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation); - }); + mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation); } - /** - * The interface for calls from outside the Shell, within the host process. - */ private class StartingSurfaceImpl implements StartingSurface { - private IStartingWindowImpl mIStartingWindow; @Override - public IStartingWindowImpl createExternalInterface() { - if (mIStartingWindow != null) { - mIStartingWindow.invalidate(); - } - mIStartingWindow = new IStartingWindowImpl(StartingWindowController.this); - return mIStartingWindow; + public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { + mSplashScreenExecutor.execute(() -> + StartingWindowController.this.addStartingWindow(windowInfo, appToken)); } - } - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class IStartingWindowImpl extends IStartingWindow.Stub { - private StartingWindowController mController; - private IStartingWindowListener mListener; - private final BiConsumer<Integer, Integer> mStartingWindowListener = - this::notifyIStartingWindowListener; - private final IBinder.DeathRecipient mListenerDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - final StartingWindowController controller = mController; - controller.getRemoteCallExecutor().execute(() -> { - mListener = null; - controller.setStartingWindowListener(null); - }); - } - }; - - public IStartingWindowImpl(StartingWindowController controller) { - mController = controller; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mController = null; + @Override + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { + mSplashScreenExecutor.execute(() -> + StartingWindowController.this.removeStartingWindow(taskId, leash, frame, + playRevealAnimation)); } @Override - public void setStartingWindowListener(IStartingWindowListener listener) { - executeRemoteCallWithTaskPermission(mController, "setStartingWindowListener", - (controller) -> { - if (mListener != null) { - // Reset the old death recipient - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - if (listener != null) { - try { - listener.asBinder().linkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death"); - return; - } - } - mListener = listener; - controller.setStartingWindowListener(mStartingWindowListener); - }); + public void copySplashScreenView(int taskId) { + mSplashScreenExecutor.execute(() -> + StartingWindowController.this.copySplashScreenView(taskId)); } - private void notifyIStartingWindowListener(int taskId, int supportedType) { - if (mListener == null) { - return; - } - - try { - mListener.onTaskLaunching(taskId, supportedType); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to notify task launching", e); - } + @Override + public void setStartingWindowListener(BiConsumer<Integer, Integer> listener) { + mSplashScreenExecutor.execute(() -> + StartingWindowController.this.setStartingWindowListener(listener)); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl deleted file mode 100644 index dffc700a3690..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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.transition; - -import android.window.IRemoteTransition; -import android.window.TransitionFilter; - -/** - * Interface that is exposed to remote callers to manipulate the transitions feature. - */ -interface IShellTransitions { - - /** - * Registers a remote transition handler. - */ - oneway void registerRemote(in TransitionFilter filter, - in IRemoteTransition remoteTransition) = 1; - - /** - * Unregisters a remote transition handler. - */ - oneway void unregisterRemote(in IRemoteTransition remoteTransition) = 2; -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index 9667f4b6a8c5..ac93a17b4014 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -16,8 +16,6 @@ package com.android.wm.shell.transition; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; - import android.annotation.NonNull; import android.annotation.Nullable; import android.os.IBinder; @@ -25,7 +23,6 @@ import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; -import android.util.Slog; import android.view.SurfaceControl; import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; @@ -34,8 +31,6 @@ import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; -import androidx.annotation.BinderThread; - import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -47,8 +42,6 @@ import java.util.ArrayList; * if the request includes a specific remote. */ public class RemoteTransitionHandler implements Transitions.TransitionHandler { - private static final String TAG = "RemoteTransitionHandler"; - private final ShellExecutor mMainExecutor; /** Includes remotes explicitly requested by, eg, ActivityOptions */ @@ -58,33 +51,15 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters = new ArrayList<>(); - private final IBinder.DeathRecipient mTransitionDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - mMainExecutor.execute(() -> { - mFilters.clear(); - }); - } - }; - RemoteTransitionHandler(@NonNull ShellExecutor mainExecutor) { mMainExecutor = mainExecutor; } void addFiltered(TransitionFilter filter, IRemoteTransition remote) { - try { - remote.asBinder().linkToDeath(mTransitionDeathRecipient, 0 /* flags */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death"); - return; - } mFilters.add(new Pair<>(filter, remote)); } void removeFiltered(IRemoteTransition remote) { - remote.asBinder().unlinkToDeath(mTransitionDeathRecipient, 0 /* flags */); for (int i = mFilters.size() - 1; i >= 0; --i) { if (mFilters.get(i).second == remote) { mFilters.remove(i); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java index bc42c6e2f12c..85bbf74d56b9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java @@ -26,15 +26,7 @@ import com.android.wm.shell.common.annotations.ExternalThread; * Interface to manage remote transitions. */ @ExternalThread -public interface ShellTransitions { - - /** - * Returns a binder that can be passed to an external process to manipulate remote transitions. - */ - default IShellTransitions createExternalInterface() { - return null; - } - +public interface RemoteTransitions { /** * Registers a remote transition. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index ca1b53d4d46b..677db10d07a7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -23,8 +23,6 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; @@ -53,7 +51,6 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ExternalThread; @@ -63,7 +60,7 @@ import java.util.ArrayList; import java.util.Arrays; /** Plays transition animations */ -public class Transitions implements RemoteCallable<Transitions> { +public class Transitions { static final String TAG = "ShellTransitions"; /** Set to {@code true} to enable shell transitions. */ @@ -76,7 +73,7 @@ public class Transitions implements RemoteCallable<Transitions> { private final ShellExecutor mAnimExecutor; private final TransitionPlayerImpl mPlayerImpl; private final RemoteTransitionHandler mRemoteTransitionHandler; - private final ShellTransitionImpl mImpl = new ShellTransitionImpl(); + private final RemoteTransitionImpl mImpl = new RemoteTransitionImpl(); /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>(); @@ -90,6 +87,10 @@ public class Transitions implements RemoteCallable<Transitions> { /** Keeps track of currently tracked transitions and all the animations associated with each */ private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>(); + public static RemoteTransitions asRemoteTransitions(Transitions transitions) { + return transitions.mImpl; + } + public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull Context context, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { @@ -125,20 +126,6 @@ public class Transitions implements RemoteCallable<Transitions> { mRemoteTransitionHandler = null; } - public ShellTransitions asRemoteTransitions() { - return mImpl; - } - - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - private void dispatchAnimScaleSetting(float scale) { for (int i = mHandlers.size() - 1; i >= 0; --i) { mHandlers.get(i).setAnimScaleSetting(scale); @@ -147,8 +134,8 @@ public class Transitions implements RemoteCallable<Transitions> { /** Create an empty/non-registering transitions object for system-ui tests. */ @VisibleForTesting - public static ShellTransitions createEmptyForTesting() { - return new ShellTransitions() { + public static RemoteTransitions createEmptyForTesting() { + return new RemoteTransitions() { @Override public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter, @androidx.annotation.NonNull IRemoteTransition remoteTransition) { @@ -439,74 +426,24 @@ public class Transitions implements RemoteCallable<Transitions> { } } - /** - * The interface for calls from outside the Shell, within the host process. - */ @ExternalThread - private class ShellTransitionImpl implements ShellTransitions { - private IShellTransitionsImpl mIShellTransitions; - - @Override - public IShellTransitions createExternalInterface() { - if (mIShellTransitions != null) { - mIShellTransitions.invalidate(); - } - mIShellTransitions = new IShellTransitionsImpl(Transitions.this); - return mIShellTransitions; - } - + private class RemoteTransitionImpl implements RemoteTransitions { @Override public void registerRemote(@NonNull TransitionFilter filter, @NonNull IRemoteTransition remoteTransition) { mMainExecutor.execute(() -> { - mRemoteTransitionHandler.addFiltered(filter, remoteTransition); + Transitions.this.registerRemote(filter, remoteTransition); }); } @Override public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) { mMainExecutor.execute(() -> { - mRemoteTransitionHandler.removeFiltered(remoteTransition); + Transitions.this.unregisterRemote(remoteTransition); }); } } - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class IShellTransitionsImpl extends IShellTransitions.Stub { - private Transitions mTransitions; - - IShellTransitionsImpl(Transitions transitions) { - mTransitions = transitions; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mTransitions = null; - } - - @Override - public void registerRemote(@NonNull TransitionFilter filter, - @NonNull IRemoteTransition remoteTransition) { - executeRemoteCallWithTaskPermission(mTransitions, "registerRemote", - (transitions) -> { - transitions.mRemoteTransitionHandler.addFiltered(filter, remoteTransition); - }); - } - - @Override - public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) { - executeRemoteCallWithTaskPermission(mTransitions, "unregisterRemote", - (transitions) -> { - transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition); - }); - } - } - private class SettingsObserver extends ContentObserver { SettingsObserver() { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt index 17c51fb15b0c..bca257646e11 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt @@ -29,8 +29,7 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper @@ -60,6 +59,11 @@ class EnterSplitScreenDockActivity( } } + override val ignoredWindows: List<String> + get() = listOf(LAUNCHER_PACKAGE_NAME, WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, + splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) + @FlakyTest(bugId = 169271943) @Test fun dockedStackPrimaryBoundsIsVisible() = @@ -73,12 +77,8 @@ class EnterSplitScreenDockActivity( @FlakyTest(bugId = 178531736) @Test // b/178531736 - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, - WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, - splitScreenApp.defaultWindowName) - ) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() @Presubmit @Test @@ -91,12 +91,8 @@ class EnterSplitScreenDockActivity( @FlakyTest(bugId = 178531736) @Test // b/178531736 - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, - WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, - splitScreenApp.defaultWindowName) - ) + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt index a94fd463c624..9000f22fb03d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt @@ -30,8 +30,7 @@ import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible @@ -62,6 +61,11 @@ class EnterSplitScreenLaunchToSide( } } + override val ignoredWindows: List<String> + get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) + @FlakyTest(bugId = 169271943) @Test fun dockedStackPrimaryBoundsIsVisible() = @@ -83,12 +87,8 @@ class EnterSplitScreenLaunchToSide( @Test // TODO(b/178447631) Remove Splash Screen from white list when flicker lib // add a wait for splash screen be gone - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME, - splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName) - ) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() @Presubmit @Test @@ -104,12 +104,8 @@ class EnterSplitScreenLaunchToSide( @Presubmit @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME, - splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName) - ) + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt index 238059b484b5..219da27c653d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt @@ -22,12 +22,10 @@ import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.WALLPAPER_TITLE import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.canSplitScreen import com.android.server.wm.flicker.helpers.openQuickstep -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.dockedStackDividerIsInvisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.Assert @@ -66,28 +64,24 @@ class EnterSplitScreenNonResizableNotDock( } } + override val ignoredWindows: List<String> + get() = listOf(LAUNCHER_PACKAGE_NAME, + WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, + nonResizeableApp.defaultWindowName, + splitScreenApp.defaultWindowName) + @Test fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible() @FlakyTest(bugId = 178447631) @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, - SPLASH_SCREEN_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName) - ) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(WALLPAPER_TITLE, - LAUNCHER_PACKAGE_NAME, - SPLASH_SCREEN_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName) - ) + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() @Test fun appWindowIsVisible() { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt index acd570a3773e..9717709852d4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt @@ -30,8 +30,7 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder @@ -70,17 +69,20 @@ class ExitLegacySplitScreenFromBottom( } } + override val ignoredWindows: List<String> + get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME, + splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) + @Presubmit @Test fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(DOCKED_STACK_DIVIDER) @FlakyTest(bugId = 178447631) @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName) - ) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + super.visibleLayersShownMoreThanOneConsecutiveEntry() + } @Presubmit @Test @@ -97,11 +99,8 @@ class ExitLegacySplitScreenFromBottom( @FlakyTest(bugId = 178447631) @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName) - ) + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt index cef188695ce7..3f714bb6b6c9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt @@ -30,8 +30,7 @@ import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.dockedStackDividerIsInvisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder @@ -70,6 +69,11 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen( } } + override val ignoredWindows: List<String> + get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME, + splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) + @FlakyTest(bugId = 175687842) @Test fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible() @@ -80,11 +84,8 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen( @FlakyTest(bugId = 178447631) @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName) - ) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() @Presubmit @Test @@ -101,11 +102,8 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen( @FlakyTest(bugId = 178447631) @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName) - ) + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt index 1e89a25c06df..08d5db0f9124 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt @@ -17,11 +17,13 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.view.Surface +import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import org.junit.Test abstract class LegacySplitScreenRotateTransition( testSpec: FlickerTestParameter @@ -44,4 +46,16 @@ abstract class LegacySplitScreenRotateTransition( } } } + + @FlakyTest + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + super.visibleLayersShownMoreThanOneConsecutiveEntry() + } + + @FlakyTest + @Test + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + super.visibleWindowsShownMoreThanOneConsecutiveEntry() + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt index 7f69a66e6e82..72d6f569ab0c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt @@ -34,14 +34,13 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible import com.android.wm.shell.flicker.helpers.SimpleAppHelper import org.junit.FixMethodOrder @@ -89,6 +88,10 @@ class LegacySplitScreenToLauncher( } } + override val ignoredWindows: List<String> + get() = listOf(launcherPackageName, WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) + @Presubmit @Test fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() @@ -99,11 +102,6 @@ class LegacySplitScreenToLauncher( @Presubmit @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() @Presubmit @@ -122,8 +120,8 @@ class LegacySplitScreenToLauncher( @Presubmit @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(launcherPackageName)) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt index 91ea8716e4f0..319fde14e418 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.support.test.launcherhelper.LauncherStrategyFactory import android.view.Surface import androidx.test.platform.app.InstrumentationRegistry @@ -29,7 +30,9 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.Test abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) { protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() @@ -40,6 +43,15 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation) .launcherStrategy.supportedLauncherPackage + /** + * List of windows that are ignored when verifying that visible elements appear on 2 + * consecutive entries in the trace. + * + * b/182720234 + */ + open val ignoredWindows: List<String> = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) + protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setup { @@ -88,11 +100,26 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa } } + @Presubmit + @Test + open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoredWindows) + } + } + + @Presubmit + @Test + open fun visibleLayersShownMoreThanOneConsecutiveEntry() { + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoredWindows) + } + } + companion object { internal const val LIVE_WALLPAPER_PACKAGE_NAME = "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2" internal const val LETTERBOX_NAME = "Letterbox" internal const val TOAST_NAME = "Toast" - internal const val SPLASH_SCREEN_NAME = "Splash Screen" } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt index caafa278d297..8a1715ee2585 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt @@ -30,8 +30,7 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.layerBecomesVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder @@ -67,19 +66,20 @@ class NonResizableDismissInLegacySplitScreen( } } + override val ignoredWindows: List<String> + get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME, + splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName, + WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) + @Presubmit @Test fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName) @FlakyTest(bugId = 178447631) @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, TOAST_NAME, - splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName) - ) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() @Presubmit @Test @@ -97,13 +97,8 @@ class NonResizableDismissInLegacySplitScreen( @FlakyTest(bugId = 178447631) @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, TOAST_NAME, - splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName) - ) + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt index 543484ac9759..4c6705f6e739 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt @@ -29,8 +29,7 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.layerBecomesVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder @@ -67,6 +66,12 @@ class NonResizableLaunchInLegacySplitScreen( } } + override val ignoredWindows: List<String> + get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, + nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName, + WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) + @Presubmit @Test fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName) @@ -77,14 +82,8 @@ class NonResizableLaunchInLegacySplitScreen( @FlakyTest(bugId = 178447631) @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, - LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName) - ) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() @Presubmit @Test @@ -98,14 +97,8 @@ class NonResizableLaunchInLegacySplitScreen( @FlakyTest(bugId = 178447631) @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, - LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName) - ) + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt index d22833784bcf..8f15e5088914 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt @@ -31,8 +31,7 @@ import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.appPairsDividerBecomesVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder @@ -61,12 +60,15 @@ class OpenAppToLegacySplitScreen( } } + override val ignoredWindows: List<String> + get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) + @FlakyTest(bugId = 178447631) @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName) - ) + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() @Presubmit @Test @@ -90,10 +92,8 @@ class OpenAppToLegacySplitScreen( @FlakyTest(bugId = 178447631) @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName) - ) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() @FlakyTest(bugId = 151179149) @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt index f5174bc3cef7..f40a08ca341c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt @@ -40,8 +40,6 @@ import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.traces.layers.getVisibleBounds import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER @@ -107,8 +105,11 @@ class ResizeLegacySplitScreen( fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry() + } + } @FlakyTest(bugId = 156223549) @Test @@ -144,8 +145,8 @@ class ResizeLegacySplitScreen( testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation) @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() @Test fun topAppLayerIsAlwaysVisible() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index 2f2bbba11646..c1c4c6dd08d7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -205,7 +205,7 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); } @@ -217,12 +217,12 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } @@ -234,12 +234,12 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } @@ -251,7 +251,7 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); } @@ -263,7 +263,7 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); } @@ -276,13 +276,13 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } @@ -295,13 +295,13 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index c5221dee9216..b21276f5401b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -121,8 +121,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { final OneHandedAnimationController animationController = new OneHandedAnimationController( mContext); OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer( - mContext, mWindowManager, mMockDisplayController, animationController, - mMockTutorialHandler, mMockBackgroundOrganizer, mMockShellMainExecutor); + mContext, mWindowManager, animationController, mMockTutorialHandler, + mMockBackgroundOrganizer, mMockShellMainExecutor); assertThat(displayAreaOrganizer.isInOneHanded()).isFalse(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java index 1fa1e2ff69b6..f897b09f8b8b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java @@ -122,7 +122,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { mSpiedDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext, mWindowManager, - mMockDisplayController, mMockAnimationController, mTutorialHandler, mMockBackgroundOrganizer, @@ -170,6 +169,15 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { } @Test + public void testRotation_getNewDisplayBounds() { + when(mMockLeash.isValid()).thenReturn(false); + // Rotate 0 -> 90 + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90, + mMockWindowContainerTransaction); + verify(mSpiedDisplayAreaOrganizer).getDisplayBounds(); + } + + @Test public void testRotation_portrait_0_to_landscape_90() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 0 -> 90 diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 971a53a8b2dc..e58f31fd15eb 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -126,7 +126,7 @@ bool Properties::load() { SkAndroidFrameworkTraceUtil::setEnableTracing( base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false)); - runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false); + runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index dcb79babad24..42aa87b492e2 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -160,7 +160,7 @@ enum DebugLevel { /** * Property for whether this is running in the emulator. */ -#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu" +#define PROPERTY_IS_EMULATOR "ro.boot.qemu" /////////////////////////////////////////////////////////////////////////////// // Misc diff --git a/location/java/android/location/CorrelationVector.java b/location/java/android/location/CorrelationVector.java index 4b6e6882b50a..718977ff4296 100644 --- a/location/java/android/location/CorrelationVector.java +++ b/location/java/android/location/CorrelationVector.java @@ -77,6 +77,9 @@ public final class CorrelationVector implements Parcelable { * be encoded as signed 16 bit integer where 1 is represented by 32767 and -1 is represented * by -32768. * + * <p>The values are quantized using a 16bit integer to save on the data size since the array + * contains real data and it might grow. + * */ @NonNull public int[] getMagnitude() { diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java index 84a363d25c63..dbf26214066e 100644 --- a/location/java/android/location/GnssNavigationMessage.java +++ b/location/java/android/location/GnssNavigationMessage.java @@ -363,10 +363,10 @@ public final class GnssNavigationMessage implements Parcelable { * <p>The bytes (or words) specified using big endian format (MSB first). * * <ul> - * <li>For GPS L1 C/A, Beidou D1 & Beidou D2, each subframe contains 10 30-bit words. Each - * word (30 bits) should be fit into the last 30 bits in a 4-byte word (skip B31 and B32), with - * MSB first, for a total of 40 bytes, covering a time period of 6, 6, and 0.6 seconds, - * respectively.</li> + * <li>For GPS L1 C/A, IRNSS L5 C/A, Beidou D1 & Beidou D2, each subframe contains 10 + * 30-bit words. Each word (30 bits) should be fit into the last 30 bits in a 4-byte word (skip + * B31 and B32), with MSB first, for a total of 40 bytes, covering a time period of 6, 6, and + * 0.6 seconds, respectively.</li> * <li>For Glonass L1 C/A, each string contains 85 data bits, including the checksum. These * bits should be fit into 11 bytes, with MSB first (skip B86-B88), covering a time period of 2 * seconds.</li> diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index b0736389b906..1b743679e585 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -385,8 +385,7 @@ public class ImageWriter implements AutoCloseable { * (acquired via {@link #dequeueInputImage}). In the former case, the Image * data will be moved to this ImageWriter. Note that the Image properties * (size, format, strides, etc.) must be the same as the properties of the - * images dequeued from this ImageWriter, or this method will throw an - * {@link IllegalArgumentException}. In the latter case, the application has + * images dequeued from this ImageWriter. In the latter case, the application has * filled the input image with data. This method then passes the filled * buffer to the downstream consumer. In both cases, it's up to the caller * to ensure that the Image timestamp (in nanoseconds) is correctly set, as diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index b4db3055e58c..02fa0401e586 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -21,6 +21,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; @@ -89,6 +90,7 @@ public final class MediaRouter2 { private final CopyOnWriteArrayList<ControllerCreationRequest> mControllerCreationRequests = new CopyOnWriteArrayList<>(); + // TODO: Specify the fields that are only used (or not used) by system media router. private final String mClientPackageName; private final ManagerCallback mManagerCallback; @@ -132,18 +134,34 @@ public final class MediaRouter2 { } /** - * Gets an instance of the media router which controls the app's media routing. + * Gets an instance of the system media router which controls the app's media routing. * Returns {@code null} if the given package name is invalid. + * There are several things to note when using the media routers created with this method. * <p> - * Note: For media routers created with this method, the discovery preference passed to - * {@link #registerRouteCallback} will have no effect. The callback will be called accordingly - * with the client app's discovery preference. Therefore, it is recommended to pass + * First of all, the discovery preference passed to {@link #registerRouteCallback} + * will have no effect. The callback will be called accordingly with the client app's + * discovery preference. Therefore, it is recommended to pass * {@link RouteDiscoveryPreference#EMPTY} there. + * <p> + * Also, do not keep/compare the instances of the {@link RoutingController}, since they are + * always newly created with the latest session information whenever below methods are called: + * <ul> + * <li> {@link #getControllers()} </li> + * <li> {@link #getController(String)}} </li> + * <li> {@link TransferCallback#onTransfer(RoutingController, RoutingController)} </li> + * <li> {@link TransferCallback#onStop(RoutingController)} </li> + * <li> {@link ControllerCallback#onControllerUpdated(RoutingController)} </li> + * </ul> + * Therefore, in order to track the current routing status, keep the controller's ID instead, + * and use {@link #getController(String)} and {@link #getSystemController()} for + * getting controllers. + * <p> + * Finally, it will have no effect to call {@link #setOnGetControllerHintsListener}. * * @param clientPackageName the package name of the app to control * @hide */ - //@SystemApi + @SystemApi @Nullable public static MediaRouter2 getInstance(@NonNull Context context, @NonNull String clientPackageName) { @@ -168,12 +186,40 @@ public final class MediaRouter2 { instance = new MediaRouter2(context, clientPackageName); sSystemMediaRouter2Map.put(clientPackageName, instance); // TODO: Remove router instance once it is not needed. - instance.registerManagerCallback(); + instance.registerManagerCallbackForSystemRouter(); } return instance; } } + /** + * Starts scanning remote routes. + * Note that calling start/stopScan is applied to all system routers in the same process. + * + * @see #stopScan() + * @hide + */ + @SystemApi + public void startScan() { + if (isSystemRouter()) { + sManager.startScan(); + } + } + + /** + * Stops scanning remote routes to reduce resource consumption. + * Note that calling start/stopScan is applied to all system routers in the same process. + * + * @see #startScan() + * @hide + */ + @SystemApi + public void stopScan() { + if (isSystemRouter()) { + sManager.stopScan(); + } + } + private MediaRouter2(Context appContext) { mContext = appContext; mMediaRouterService = IMediaRouterService.Stub.asInterface( @@ -209,13 +255,19 @@ public final class MediaRouter2 { } private MediaRouter2(Context context, String clientPackageName) { + mContext = context; mClientPackageName = clientPackageName; mManagerCallback = new ManagerCallback(); - mContext = context; - mMediaRouterService = null; - mPackageName = null; mHandler = new Handler(Looper.getMainLooper()); - mSystemController = null; + mSystemController = new SystemRoutingController( + ensureClientPackageNameForSystemSession(sManager.getSystemRoutingSession())); + mDiscoveryPreference = new RouteDiscoveryPreference.Builder( + sManager.getPreferredFeatures(clientPackageName), true).build(); + updateAllRoutesFromManager(); + mMediaRouterService = null; // TODO: Make this non-null and check permission. + + // Only used by non-system MediaRouter2. + mPackageName = null; } /** @@ -240,7 +292,7 @@ public final class MediaRouter2 { * @see #getInstance(Context, String) * @hide */ - //@SystemApi + @SystemApi @Nullable public String getClientPackageName() { return mClientPackageName; @@ -358,7 +410,8 @@ public final class MediaRouter2 { * * @hide */ - //@SystemApi + @SystemApi + @NonNull public List<MediaRoute2Info> getAllRoutes() { if (isSystemRouter()) { return sManager.getAllRoutes(); @@ -377,10 +430,6 @@ public final class MediaRouter2 { */ @NonNull public List<MediaRoute2Info> getRoutes() { - if (isSystemRouter()) { - return sManager.getAvailableRoutes(mClientPackageName); - } - synchronized (mLock) { if (mShouldUpdateRoutes) { mShouldUpdateRoutes = false; @@ -474,6 +523,9 @@ public final class MediaRouter2 { * {@code null} for unset. */ public void setOnGetControllerHintsListener(@Nullable OnGetControllerHintsListener listener) { + if (isSystemRouter()) { + return; + } mOnGetControllerHintsListener = listener; } @@ -519,7 +571,7 @@ public final class MediaRouter2 { * @param route the route you want to transfer the media to. * @hide */ - //@SystemApi + @SystemApi public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) { if (isSystemRouter()) { sManager.transfer(controller.getRoutingSessionInfo(), route); @@ -606,6 +658,23 @@ public final class MediaRouter2 { } /** + * Gets a {@link RoutingController} whose ID is equal to the given ID. + * Returns {@code null} if there is no matching controller. + * @hide + */ + @SystemApi + @Nullable + public RoutingController getController(@NonNull String id) { + Objects.requireNonNull(id, "id must not be null"); + for (RoutingController controller : getControllers()) { + if (TextUtils.equals(id, controller.getId())) { + return controller; + } + } + return null; + } + + /** * Gets the list of currently active {@link RoutingController routing controllers} on which * media can be played. * <p> @@ -614,15 +683,26 @@ public final class MediaRouter2 { */ @NonNull public List<RoutingController> getControllers() { - // TODO: Do not create the controller instances every time, - // Instead, update the list using the sessions' ID and session related callbacks. + List<RoutingController> result = new ArrayList<>(); + if (isSystemRouter()) { - return sManager.getRoutingSessions(mClientPackageName).stream() - .map(info -> new RoutingController(info)) - .collect(Collectors.toList()); + // Unlike non-system MediaRouter2, controller instances cannot be kept, + // since the transfer events initiated from other apps will not come through manager. + List<RoutingSessionInfo> sessions = sManager.getRoutingSessions(mClientPackageName); + for (RoutingSessionInfo session : sessions) { + RoutingController controller; + if (session.isSystemSession()) { + mSystemController.setRoutingSessionInfo( + ensureClientPackageNameForSystemSession(session)); + controller = mSystemController; + } else { + controller = new RoutingController(session); + } + result.add(controller); + } + return result; } - List<RoutingController> result = new ArrayList<>(); result.add(0, mSystemController); synchronized (mLock) { result.addAll(mNonSystemRoutingControllers.values()); @@ -639,9 +719,15 @@ public final class MediaRouter2 { * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}. * @hide */ + @SystemApi public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) { Objects.requireNonNull(route, "route must not be null"); + if (isSystemRouter()) { + sManager.setRouteVolume(route, volume); + return; + } + MediaRouter2Stub stub; synchronized (mLock) { stub = mStub; @@ -928,12 +1014,30 @@ public final class MediaRouter2 { /** * Registers {@link MediaRouter2Manager.Callback} for getting events. + * Should only used for system media routers. */ - private void registerManagerCallback() { + private void registerManagerCallbackForSystemRouter() { // Using direct executor here, since MediaRouter2Manager also posts to the main handler. sManager.registerCallback(Runnable::run, mManagerCallback); } + /** + * Returns a {@link RoutingSessionInfo} which has the client package name. + * The client package name is set only when the given sessionInfo doesn't have it. + * Should only used for system media routers. + */ + private RoutingSessionInfo ensureClientPackageNameForSystemSession( + @NonNull RoutingSessionInfo sessionInfo) { + if (!sessionInfo.isSystemSession() + || !TextUtils.isEmpty(sessionInfo.getClientPackageName())) { + return sessionInfo; + } + + return new RoutingSessionInfo.Builder(sessionInfo) + .setClientPackageName(mClientPackageName) + .build(); + } + private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes, RouteDiscoveryPreference discoveryRequest) { return routes.stream() @@ -941,6 +1045,16 @@ public final class MediaRouter2 { .collect(Collectors.toList()); } + private void updateAllRoutesFromManager() { + synchronized (mLock) { + mRoutes.clear(); + for (MediaRoute2Info route : sManager.getAllRoutes()) { + mRoutes.put(route.getId(), route); + } + mShouldUpdateRoutes = true; + } + } + private void notifyRoutesAdded(List<MediaRoute2Info> routes) { for (RouteCallbackRecord record: mRouteCallbackRecords) { List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference); @@ -971,6 +1085,13 @@ public final class MediaRouter2 { } } + private void notifyPreferredFeaturesChanged(List<String> features) { + for (RouteCallbackRecord record: mRouteCallbackRecords) { + record.mExecutor.execute( + () -> record.mRouteCallback.onPreferredFeaturesChanged(features)); + } + } + private void notifyTransfer(RoutingController oldController, RoutingController newController) { for (TransferCallbackRecord record: mTransferCallbackRecords) { record.mExecutor.execute( @@ -1024,6 +1145,17 @@ public final class MediaRouter2 { * @param routes the list of routes that have been changed. It's never empty. */ public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} + + /** + * Called when the client app's preferred features are changed. + * When this is called, it is recommended to {@link #getRoutes()} to get the routes + * that are currently available to the app. + * + * @param preferredFeatures the new preferred features set by the application + * @hide + */ + @SystemApi + public void onPreferredFeaturesChanged(@NonNull List<String> preferredFeatures) {} } /** @@ -1131,6 +1263,11 @@ public final class MediaRouter2 { mState = CONTROLLER_STATE_ACTIVE; } + RoutingController(@NonNull RoutingSessionInfo sessionInfo, int state) { + mSessionInfo = sessionInfo; + mState = state; + } + /** * @return the ID of the controller. It is globally unique. */ @@ -1291,6 +1428,11 @@ public final class MediaRouter2 { return; } + if (isSystemRouter()) { + sManager.selectRoute(getRoutingSessionInfo(), route); + return; + } + MediaRouter2Stub stub; synchronized (mLock) { stub = mStub; @@ -1338,6 +1480,11 @@ public final class MediaRouter2 { return; } + if (isSystemRouter()) { + sManager.deselectRoute(getRoutingSessionInfo(), route); + return; + } + MediaRouter2Stub stub; synchronized (mLock) { stub = mStub; @@ -1407,6 +1554,12 @@ public final class MediaRouter2 { Log.w(TAG, "setVolume: Called on released controller. Ignoring."); return; } + + if (isSystemRouter()) { + sManager.setSessionVolume(getRoutingSessionInfo(), volume); + return; + } + MediaRouter2Stub stub; synchronized (mLock) { stub = mStub; @@ -1471,6 +1624,11 @@ public final class MediaRouter2 { mState = CONTROLLER_STATE_RELEASED; } + if (isSystemRouter()) { + sManager.releaseSession(getRoutingSessionInfo()); + return; + } + synchronized (mLock) { mNonSystemRoutingControllers.remove(getId(), this); @@ -1539,6 +1697,12 @@ public final class MediaRouter2 { } private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) { + if (isSystemRouter()) { + return getRoutes().stream() + .filter(r -> routeIds.contains(r.getId())) + .collect(Collectors.toList()); + } + synchronized (mLock) { return routeIds.stream().map(mRoutes::get) .filter(Objects::nonNull) @@ -1722,12 +1886,17 @@ public final class MediaRouter2 { } } + // Note: All methods are run on main thread. class ManagerCallback implements MediaRouter2Manager.Callback { @Override public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) { - List<MediaRoute2Info> filteredRoutes = - sManager.filterRoutesForPackage(routes, mClientPackageName); + updateAllRoutesFromManager(); + + List<MediaRoute2Info> filteredRoutes; + synchronized (mLock) { + filteredRoutes = filterRoutes(routes, mDiscoveryPreference); + } if (filteredRoutes.isEmpty()) { return; } @@ -1739,8 +1908,12 @@ public final class MediaRouter2 { @Override public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) { - List<MediaRoute2Info> filteredRoutes = - sManager.filterRoutesForPackage(routes, mClientPackageName); + updateAllRoutesFromManager(); + + List<MediaRoute2Info> filteredRoutes; + synchronized (mLock) { + filteredRoutes = filterRoutes(routes, mDiscoveryPreference); + } if (filteredRoutes.isEmpty()) { return; } @@ -1752,8 +1925,12 @@ public final class MediaRouter2 { @Override public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) { - List<MediaRoute2Info> filteredRoutes = - sManager.filterRoutesForPackage(routes, mClientPackageName); + updateAllRoutesFromManager(); + + List<MediaRoute2Info> filteredRoutes; + synchronized (mLock) { + filteredRoutes = filterRoutes(routes, mDiscoveryPreference); + } if (filteredRoutes.isEmpty()) { return; } @@ -1764,31 +1941,101 @@ public final class MediaRouter2 { } @Override - public void onSessionUpdated(@NonNull RoutingSessionInfo session) { - // TODO: Call ControllerCallback.onControllerUpdated - } - - @Override public void onTransferred(@NonNull RoutingSessionInfo oldSession, - @Nullable RoutingSessionInfo newSession) { - // TODO: Call TransferCallback.onTransfer + @NonNull RoutingSessionInfo newSession) { + if (!oldSession.isSystemSession() + && !TextUtils.equals(mClientPackageName, oldSession.getClientPackageName())) { + return; + } + + if (!newSession.isSystemSession() + && !TextUtils.equals(mClientPackageName, newSession.getClientPackageName())) { + return; + } + + // For successful in-session transfer, onControllerUpdated() handles it. + if (TextUtils.equals(oldSession.getId(), newSession.getId())) { + return; + } + + + RoutingController oldController; + if (oldSession.isSystemSession()) { + mSystemController.setRoutingSessionInfo( + ensureClientPackageNameForSystemSession(oldSession)); + oldController = mSystemController; + } else { + oldController = new RoutingController(oldSession); + } + + RoutingController newController; + if (newSession.isSystemSession()) { + mSystemController.setRoutingSessionInfo( + ensureClientPackageNameForSystemSession(newSession)); + newController = mSystemController; + } else { + newController = new RoutingController(newSession); + } + + notifyTransfer(oldController, newController); } @Override public void onTransferFailed(@NonNull RoutingSessionInfo session, @NonNull MediaRoute2Info route) { - // TODO: Call TransferCallback.onTransferFailure + if (!session.isSystemSession() + && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) { + return; + } + notifyTransferFailure(route); + } + + @Override + public void onSessionUpdated(@NonNull RoutingSessionInfo session) { + if (!session.isSystemSession() + && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) { + return; + } + + RoutingController controller; + if (session.isSystemSession()) { + mSystemController.setRoutingSessionInfo( + ensureClientPackageNameForSystemSession(session)); + controller = mSystemController; + } else { + controller = new RoutingController(session); + } + notifyControllerUpdated(controller); } @Override public void onSessionReleased(@NonNull RoutingSessionInfo session) { - // TODO: Call TransferCallback.onStop() + if (session.isSystemSession()) { + Log.e(TAG, "onSessionReleased: Called on system session. Ignoring."); + return; + } + + if (!TextUtils.equals(mClientPackageName, session.getClientPackageName())) { + return; + } + + notifyStop(new RoutingController(session, RoutingController.CONTROLLER_STATE_RELEASED)); } @Override public void onPreferredFeaturesChanged(@NonNull String packageName, @NonNull List<String> preferredFeatures) { - // Does nothing. + if (!TextUtils.equals(mClientPackageName, packageName)) { + return; + } + + synchronized (mLock) { + mDiscoveryPreference = new RouteDiscoveryPreference.Builder( + preferredFeatures, true).build(); + } + + updateAllRoutesFromManager(); + notifyPreferredFeaturesChanged(preferredFeatures); } @Override diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index ca619d4072c3..6fefbe15abae 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -148,7 +148,7 @@ public final class MediaRouter2Manager { /** * Starts scanning remote routes. - * @see #stopScan(String) + * @see #stopScan() */ public void startScan() { Client client = getOrCreateClient(); @@ -163,7 +163,7 @@ public final class MediaRouter2Manager { /** * Stops scanning remote routes to reduce resource consumption. - * @see #startScan(String) + * @see #startScan() */ public void stopScan() { Client client = getOrCreateClient(); @@ -237,6 +237,20 @@ public final class MediaRouter2Manager { } /** + * Returns the preferred features of the specified package name. + */ + @NonNull + public List<String> getPreferredFeatures(@NonNull String packageName) { + Objects.requireNonNull(packageName, "packageName must not be null"); + + List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName); + if (preferredFeatures == null) { + preferredFeatures = Collections.emptyList(); + } + return preferredFeatures; + } + + /** * Returns a list of routes which are related to the given package name in the given route list. */ @NonNull @@ -788,8 +802,8 @@ public final class MediaRouter2Manager { * Requests releasing a session. * <p> * If a session is released, any operation on the session will be ignored. - * {@link Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)} with {@code null} - * session will be called when the session is released. + * {@link Callback#onSessionReleased(RoutingSessionInfo)} will be called + * when the session is released. * </p> * * @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo) @@ -945,10 +959,10 @@ public final class MediaRouter2Manager { * Called when media is transferred. * * @param oldSession the previous session - * @param newSession the new session or {@code null} if the session is released. + * @param newSession the new session */ default void onTransferred(@NonNull RoutingSessionInfo oldSession, - @Nullable RoutingSessionInfo newSession) { } + @NonNull RoutingSessionInfo newSession) { } /** * Called when {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} fails. diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl new file mode 100644 index 000000000000..ceef73cd194a --- /dev/null +++ b/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl @@ -0,0 +1,10 @@ +package android.media.musicrecognition; + +/** + * Interface from {@link MusicRecognitionService} to system to pass attribution tag. + * + * @hide + */ +oneway interface IMusicRecognitionAttributionTagCallback { + void onAttributionTag(in String attributionTag); +}
\ No newline at end of file diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl index 26543ed8bf8c..c970161a115b 100644 --- a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl +++ b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl @@ -4,6 +4,7 @@ import android.media.AudioFormat; import android.os.ParcelFileDescriptor; import android.os.IBinder; import android.media.musicrecognition.IMusicRecognitionServiceCallback; +import android.media.musicrecognition.IMusicRecognitionAttributionTagCallback; /** * Interface from the system to a {@link MusicRecognitionService}. @@ -15,4 +16,6 @@ oneway interface IMusicRecognitionService { in ParcelFileDescriptor fd, in AudioFormat audioFormat, in IMusicRecognitionServiceCallback callback); + + void getAttributionTag(in IMusicRecognitionAttributionTagCallback callback); }
\ No newline at end of file diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl index 15215c4e15f1..10a65545cc7e 100644 --- a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl +++ b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl @@ -4,7 +4,7 @@ import android.os.Bundle; import android.media.MediaMetadata; /** - * Interface from a {@MusicRecognitionService} the system. + * Interface from a {@MusicRecognitionService} to the system. * * @hide */ diff --git a/media/java/android/media/musicrecognition/MusicRecognitionService.java b/media/java/android/media/musicrecognition/MusicRecognitionService.java index 04b4c39bf0fa..385aff01d45a 100644 --- a/media/java/android/media/musicrecognition/MusicRecognitionService.java +++ b/media/java/android/media/musicrecognition/MusicRecognitionService.java @@ -90,7 +90,7 @@ public abstract class MusicRecognitionService extends Service { try { callback.onRecognitionSucceeded(result, extras); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -99,11 +99,18 @@ public abstract class MusicRecognitionService extends Service { try { callback.onRecognitionFailed(failureCode); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } })); } + + @Override + public void getAttributionTag( + IMusicRecognitionAttributionTagCallback callback) throws RemoteException { + String tag = MusicRecognitionService.this.getAttributionTag(); + callback.onAttributionTag(tag); + } }; @Override diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 1870a939f0dc..7f5dd5d15dbe 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -2260,6 +2260,8 @@ static void android_media_MediaCodec_native_queueHardwareBuffer( c2_status_t c2err = sGrallocAlloc->priorGraphicAllocation(handle, &alloc); if (c2err != C2_OK) { ALOGW("Failed to wrap AHardwareBuffer into C2GraphicAllocation"); + native_handle_close(handle); + native_handle_delete(handle); throwExceptionAsNecessary(env, BAD_VALUE); return; } diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp index 8b84bf3eb8d8..309d71cfd062 100644 --- a/media/jni/soundpool/StreamManager.cpp +++ b/media/jni/soundpool/StreamManager.cpp @@ -43,6 +43,14 @@ static constexpr bool kPlayOnCallingThread = true; // Amount of time for a StreamManager thread to wait before closing. static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND; +// Debug flag: +// kForceLockStreamManagerStop is set to true to force lock the StreamManager +// worker thread during stop. This limits concurrency of Stream processing. +// Normally we lock the StreamManager worker thread during stop ONLY +// for SoundPools configured with a single Stream. +// +static constexpr bool kForceLockStreamManagerStop = false; + //////////// StreamMap::StreamMap(int32_t streams) { @@ -103,6 +111,7 @@ StreamManager::StreamManager( : StreamMap(streams) , mAttributes(*attributes) , mOpPackageName(std::move(opPackageName)) + , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop) { ALOGV("%s(%d, %zu, ...)", __func__, streams, threads); forEach([this](Stream *stream) { @@ -113,7 +122,8 @@ StreamManager::StreamManager( }); mThreadPool = std::make_unique<ThreadPool>( - std::min(threads, (size_t)std::thread::hardware_concurrency()), + std::min((size_t)streams, // do not make more threads than streams to play + std::min(threads, (size_t)std::thread::hardware_concurrency())), "SoundPool_"); } @@ -348,14 +358,14 @@ void StreamManager::addToActiveQueue_l(Stream *stream) { void StreamManager::run(int32_t id) { ALOGV("%s(%d) entering", __func__, id); - int64_t waitTimeNs = kWaitTimeBeforeCloseNs; + int64_t waitTimeNs = 0; // on thread start, mRestartStreams can be non-empty. std::unique_lock lock(mStreamManagerLock); while (!mQuit) { - if (mRestartStreams.empty()) { // on thread start, mRestartStreams can be non-empty. + if (waitTimeNs > 0) { mStreamManagerCondition.wait_for( lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs)); } - ALOGV("%s(%d) awake", __func__, id); + ALOGV("%s(%d) awake lock waitTimeNs:%lld", __func__, id, (long long)waitTimeNs); sanityCheckQueue_l(); @@ -375,12 +385,12 @@ void StreamManager::run(int32_t id) } mRestartStreams.erase(it); mProcessingStreams.emplace(stream); - lock.unlock(); + if (!mLockStreamManagerStop) lock.unlock(); stream->stop(); ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID()); if (Stream* nextStream = stream->playPairStream()) { ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID()); - lock.lock(); + if (!mLockStreamManagerStop) lock.lock(); if (nextStream->getStopTimeNs() > 0) { // the next stream was stopped before we can move it to the active queue. ALOGV("%s(%d) stopping started streamID:%d", @@ -390,7 +400,7 @@ void StreamManager::run(int32_t id) addToActiveQueue_l(nextStream); } } else { - lock.lock(); + if (!mLockStreamManagerStop) lock.lock(); mAvailableStreams.insert(stream); } mProcessingStreams.erase(stream); diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h index 81ac69eb4358..85b468cd2cbc 100644 --- a/media/jni/soundpool/StreamManager.h +++ b/media/jni/soundpool/StreamManager.h @@ -437,6 +437,14 @@ private: void sanityCheckQueue_l() const REQUIRES(mStreamManagerLock); const audio_attributes_t mAttributes; + const std::string mOpPackageName; + + // For legacy compatibility, we lock the stream manager on stop when + // there is only one stream. This allows a play to be called immediately + // after stopping, otherwise it is possible that the play might be discarded + // (returns 0) because that stream may be in the worker thread call to stop. + const bool mLockStreamManagerStop; + std::unique_ptr<ThreadPool> mThreadPool; // locked internally // mStreamManagerLock is used to lock access for transitions between the @@ -477,8 +485,6 @@ private: // The paired stream may be active or restarting. // No particular order. std::unordered_set<Stream*> mProcessingStreams GUARDED_BY(mStreamManagerLock); - - const std::string mOpPackageName; }; } // namespace android::soundpool diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp index 779318008a9b..0476216dcbed 100644 --- a/media/jni/tuner/DvrClient.cpp +++ b/media/jni/tuner/DvrClient.cpp @@ -314,6 +314,11 @@ Result DvrClient::flush() { } Result DvrClient::close() { + if (mDvrMQEventFlag != NULL) { + EventFlag::deleteEventFlag(&mDvrMQEventFlag); + } + mDvrMQ = NULL; + if (mTunerDvr != NULL) { Status s = mTunerDvr->close(); mTunerDvr = NULL; diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp index f31d4651350a..8846e4d62522 100644 --- a/media/jni/tuner/FilterClient.cpp +++ b/media/jni/tuner/FilterClient.cpp @@ -259,6 +259,11 @@ Result FilterClient::setDataSource(sp<FilterClient> filterClient){ } Result FilterClient::close() { + if (mFilterMQEventFlag != NULL) { + EventFlag::deleteEventFlag(&mFilterMQEventFlag); + } + mFilterMQ = NULL; + if (mTunerFilter != NULL) { Status s = mTunerFilter->close(); closeAvSharedMemory(); diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java index 6fab9e4641b6..5afe0b5d64bc 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java @@ -50,7 +50,7 @@ import android.widget.ProgressBar; import android.widget.TextView; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.TrafficStatsConstants; +import com.android.net.module.util.NetworkStackConstants; import java.io.IOException; import java.lang.reflect.Field; @@ -239,7 +239,7 @@ public class CaptivePortalLoginActivity extends Activity { HttpURLConnection urlConnection = null; int httpResponseCode = 500; int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_PROBE); + NetworkStackConstants.TAG_SYSTEM_PROBE); try { urlConnection = (HttpURLConnection) mNetwork.openConnection( new URL(mCm.getCaptivePortalServerUrl())); diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index 4dd012a8d074..44748e9cc692 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -29,7 +29,7 @@ <string name="profile_name_watch">watch</string> <!-- Title of the device association confirmation dialog. --> - <string name="confirmation_title">Set <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong></string> + <string name="confirmation_title">Allow <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong></string> <!-- Text of the device profile permissions explanation in the association dialog. --> <string name="profile_summary">This app is needed to manage your <xliff:g id="profile_name" example="watch">%1$s</xliff:g>. <xliff:g id="privileges_discplaimer" example="Android Wear will get access to your Notifications, Calendar and Contacts.">%2$s</xliff:g></string> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index f1a98adc0ab2..91a6749ac42d 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -128,9 +128,11 @@ public class CompanionDeviceActivity extends Activity { if (useDeviceProfile) { profileSummary.setVisibility(View.VISIBLE); + String deviceRef = getRequest().isSingleDevice() + ? getService().mDevicesFound.get(0).getDisplayName() + : profileName; profileSummary.setText(getString(R.string.profile_summary, - getCallingAppName(), - profileName, + deviceRef, profilePrivacyDisclaimer)); } else { profileSummary.setVisibility(View.GONE); diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index a04571496cce..cc95a7cc2413 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -8,7 +8,7 @@ package android.net { public class ConnectivityManager { method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot(); method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange(); - method @NonNull public static String getPrivateDnsMode(@NonNull android.content.ContentResolver); + method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); @@ -29,6 +29,11 @@ package android.net { field public static final int TRANSPORT_TEST = 7; // 0x7 } + public class ParseException extends java.lang.RuntimeException { + ctor public ParseException(@NonNull String); + ctor public ParseException(@NonNull String, @NonNull Throwable); + } + public final class TcpRepairWindow { ctor public TcpRepairWindow(int, int, int, int, int, int); field public final int maxWindow; diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 66c45ed6fcd7..7bfc9702ba6a 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -18,8 +18,8 @@ package android.net; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; import static android.net.NetworkRequest.Type.LISTEN; +import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST; import static android.net.NetworkRequest.Type.REQUEST; -import static android.net.NetworkRequest.Type.TRACK_BEST; import static android.net.NetworkRequest.Type.TRACK_DEFAULT; import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT; import static android.net.QosCallback.QosCallbackRegistrationException; @@ -3213,10 +3213,6 @@ public class ConnectivityManager { } } - // TODO : remove this method. It is a stopgap measure to help sheperding a number - // of dependent changes that would conflict throughout the automerger graph. Having this - // temporarily helps with the process of going through with all these dependent changes across - // the entire tree. /** * @hide * Register a NetworkAgent with ConnectivityService. @@ -3226,20 +3222,8 @@ public class ConnectivityManager { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp, - NetworkCapabilities nc, int score, NetworkAgentConfig config) { - return registerNetworkAgent(na, ni, lp, nc, score, config, NetworkProvider.ID_NONE); - } - - /** - * @hide - * Register a NetworkAgent with ConnectivityService. - * @return Network corresponding to NetworkAgent. - */ - @RequiresPermission(anyOf = { - NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - android.Manifest.permission.NETWORK_FACTORY}) - public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp, - NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) { + NetworkCapabilities nc, @NonNull NetworkScore score, NetworkAgentConfig config, + int providerId) { try { return mService.registerNetworkAgent(na, ni, lp, nc, score, config, providerId); } catch (RemoteException e) { @@ -4265,7 +4249,7 @@ public class ConnectivityManager { @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { final NetworkCapabilities nc = request.networkCapabilities; final CallbackHandler cbHandler = new CallbackHandler(handler); - sendRequestForNetwork(nc, networkCallback, 0, TRACK_BEST, TYPE_NONE, cbHandler); + sendRequestForNetwork(nc, networkCallback, 0, LISTEN_FOR_BEST, TYPE_NONE, cbHandler); } /** @@ -5121,9 +5105,9 @@ public class ConnectivityManager { } // The first network ID of IPSec tunnel interface. - private static final int TUN_INTF_NETID_START = 0xFC00; + private static final int TUN_INTF_NETID_START = 0xFC00; // 0xFC00 = 64512 // The network ID range of IPSec tunnel interface. - private static final int TUN_INTF_NETID_RANGE = 0x0400; + private static final int TUN_INTF_NETID_RANGE = 0x0400; // 0x0400 = 1024 /** * Get the network ID range reserved for IPSec tunnel interfaces. @@ -5140,7 +5124,8 @@ public class ConnectivityManager { /** * Get private DNS mode from settings. * - * @param cr The ContentResolver to query private DNS mode from settings. + * @param context The Context to get its ContentResolver to query the private DNS mode from + * settings. * @return A string of private DNS mode as one of the PRIVATE_DNS_MODE_* constants. * * @hide @@ -5148,7 +5133,8 @@ public class ConnectivityManager { @SystemApi(client = MODULE_LIBRARIES) @NonNull @PrivateDnsMode - public static String getPrivateDnsMode(@NonNull ContentResolver cr) { + public static String getPrivateDnsMode(@NonNull Context context) { + final ContentResolver cr = context.getContentResolver(); String mode = Settings.Global.getString(cr, PRIVATE_DNS_MODE); if (TextUtils.isEmpty(mode)) mode = Settings.Global.getString(cr, PRIVATE_DNS_DEFAULT_MODE); // If both PRIVATE_DNS_MODE and PRIVATE_DNS_DEFAULT_MODE are not set, choose diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index f9393e315b83..1bbf1a95fcca 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -30,6 +30,7 @@ import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; +import android.net.NetworkScore; import android.net.NetworkState; import android.net.NetworkStateSnapshot; import android.net.OemNetworkPreferences; @@ -138,7 +139,7 @@ interface IConnectivityManager void declareNetworkRequestUnfulfillable(in NetworkRequest request); Network registerNetworkAgent(in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp, - in NetworkCapabilities nc, int score, in NetworkAgentConfig config, + in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config, in int factorySerialNumber); NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType, diff --git a/packages/Connectivity/framework/src/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java index 46141e0d0c1e..7245db3b17db 100644 --- a/packages/Connectivity/framework/src/android/net/Network.java +++ b/packages/Connectivity/framework/src/android/net/Network.java @@ -30,10 +30,10 @@ import android.system.OsConstants; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; -import com.android.okhttp.internalandroidapi.Dns; -import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory; import libcore.io.IoUtils; +import libcore.net.http.Dns; +import libcore.net.http.HttpURLConnectionFactory; import java.io.FileDescriptor; import java.io.IOException; @@ -299,7 +299,7 @@ public class Network implements Parcelable { // Set configuration on the HttpURLConnectionFactory that will be good for all // connections created by this Network. Configuration that might vary is left // until openConnection() and passed as arguments. - HttpURLConnectionFactory urlConnectionFactory = new HttpURLConnectionFactory(); + HttpURLConnectionFactory urlConnectionFactory = HttpURLConnectionFactory.createInstance(); urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup // A private connection pool just for this Network. urlConnectionFactory.setNewConnectionPool(httpMaxConnections, diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java index 27aa15d1e1e4..b3ab0ee8bd3c 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java +++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java @@ -371,6 +371,14 @@ public abstract class NetworkAgent { return ni; } + // Temporary backward compatibility constructor + public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag, + @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score, + @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) { + this(context, looper, logTag, nc, lp, + new NetworkScore.Builder().setLegacyInt(score).build(), config, provider); + } + /** * Create a new network agent. * @param context a {@link Context} to get system services from. @@ -382,10 +390,12 @@ public abstract class NetworkAgent { * @param score the initial score of this network. Update with sendNetworkScore. * @param config an immutable {@link NetworkAgentConfig} for this agent. * @param provider the {@link NetworkProvider} managing this agent. + * @hide TODO : unhide when impl is complete */ public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag, - @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score, - @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) { + @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, + @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, + @Nullable NetworkProvider provider) { this(looper, context, logTag, nc, lp, score, config, provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(), getLegacyNetworkInfo(config)); @@ -395,12 +405,12 @@ public abstract class NetworkAgent { public final Context context; public final NetworkCapabilities capabilities; public final LinkProperties properties; - public final int score; + public final NetworkScore score; public final NetworkAgentConfig config; public final NetworkInfo info; InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities, - @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config, - @NonNull NetworkInfo info) { + @NonNull LinkProperties properties, @NonNull NetworkScore score, + @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info) { this.context = context; this.capabilities = capabilities; this.properties = properties; @@ -412,8 +422,9 @@ public abstract class NetworkAgent { private volatile InitialConfiguration mInitialConfiguration; private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag, - @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score, - @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni) { + @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, + @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, int providerId, + @NonNull NetworkInfo ni) { mHandler = new NetworkAgentHandler(looper); LOG_TAG = logTag; mNetworkInfo = new NetworkInfo(ni); @@ -875,13 +886,22 @@ public abstract class NetworkAgent { /** * Must be called by the agent to update the score of this network. * + * @param score the new score. + * @hide TODO : unhide when impl is complete + */ + public final void sendNetworkScore(@NonNull NetworkScore score) { + Objects.requireNonNull(score); + queueOrSendMessage(reg -> reg.sendScore(score)); + } + + /** + * Must be called by the agent to update the score of this network. + * * @param score the new score, between 0 and 99. + * deprecated use sendNetworkScore(NetworkScore) TODO : remove in S. */ public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) { - if (score < 0) { - throw new IllegalArgumentException("Score must be >= 0"); - } - queueOrSendMessage(reg -> reg.sendScore(score)); + sendNetworkScore(new NetworkScore.Builder().setLegacyInt(score).build()); } /** diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index 3fd95ee58df2..dbe3ecc4d775 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -140,7 +140,7 @@ public class NetworkRequest implements Parcelable { REQUEST, BACKGROUND_REQUEST, TRACK_SYSTEM_DEFAULT, - TRACK_BEST, + LISTEN_FOR_BEST, }; /** @@ -514,6 +514,15 @@ public class NetworkRequest implements Parcelable { } /** + * Returns true iff. this NetworkRequest is of type LISTEN_FOR_BEST. + * + * @hide + */ + public boolean isListenForBest() { + return type == Type.LISTEN_FOR_BEST; + } + + /** * Returns true iff. the contained NetworkRequest is one that: * * - should be associated with at most one satisfying network diff --git a/packages/Connectivity/framework/src/android/net/ParseException.java b/packages/Connectivity/framework/src/android/net/ParseException.java index bcfdd7ef09cc..ca6d012dfe7c 100644 --- a/packages/Connectivity/framework/src/android/net/ParseException.java +++ b/packages/Connectivity/framework/src/android/net/ParseException.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.SystemApi; /** * Thrown when parsing failed. @@ -25,12 +26,16 @@ import android.annotation.NonNull; public class ParseException extends RuntimeException { public String response; - ParseException(@NonNull String response) { + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public ParseException(@NonNull String response) { super(response); this.response = response; } - ParseException(@NonNull String response, @NonNull Throwable cause) { + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public ParseException(@NonNull String response, @NonNull Throwable cause) { super(response, cause); this.response = response; } diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl index f0193db5c2e2..18d26a7e4baf 100644 --- a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl +++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl @@ -19,11 +19,12 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; +import android.net.NetworkScore; import android.net.QosSession; import android.telephony.data.EpsBearerQosSessionAttributes; /** - * Interface for NetworkAgents to send network network properties. + * Interface for NetworkAgents to send network properties. * @hide */ oneway interface INetworkAgentRegistry { @@ -31,7 +32,7 @@ oneway interface INetworkAgentRegistry { void sendLinkProperties(in LinkProperties lp); // TODO: consider replacing this by "markConnected()" and removing void sendNetworkInfo(in NetworkInfo info); - void sendScore(int score); + void sendScore(in NetworkScore score); void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial); void sendSocketKeepaliveEvent(int slot, int reason); void sendUnderlyingNetworks(in @nullable List<Network> networks); diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index f2a0e1cb83bd..68ce7d963242 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -17,8 +17,8 @@ android_library { // TODO(b/149540986): revert this change. static_libs: [ - // All other dependent components should be put in - // "SettingsLibDependenciesWithoutWifiTracker". + // All other dependent components should be put in + // "SettingsLibDependenciesWithoutWifiTracker". "WifiTrackerLib", ], @@ -27,7 +27,10 @@ android_library { resource_dirs: ["res"], - srcs: ["src/**/*.java", "src/**/*.kt"], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], min_sdk_version: "21", @@ -67,6 +70,7 @@ java_defaults { "SettingsLibFooterPreference", "SettingsLibUsageProgressBarPreference", "SettingsLibCollapsingToolbarBaseActivity", + "SettingsLibTwoTargetPreference", ], } diff --git a/packages/SettingsLib/TwoTargetPreference/Android.bp b/packages/SettingsLib/TwoTargetPreference/Android.bp new file mode 100644 index 000000000000..f2e79b9ab53b --- /dev/null +++ b/packages/SettingsLib/TwoTargetPreference/Android.bp @@ -0,0 +1,13 @@ +android_library { + name: "SettingsLibTwoTargetPreference", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + static_libs: [ + "androidx.annotation_annotation", + "androidx.preference_preference", + ], + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml b/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml new file mode 100644 index 000000000000..120b0859a70a --- /dev/null +++ b/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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.settingslib.widget"> + + <uses-sdk android:minSdkVersion="21" /> + +</manifest> diff --git a/packages/SettingsLib/res/layout/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml index ff6a22d5523c..21fcedcc01b6 100644 --- a/packages/SettingsLib/res/layout/preference_two_target.xml +++ b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2017 The Android Open Source Project + Copyright (C) 2021 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SettingsLib/res/layout/preference_two_target_divider.xml b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml index b81dd83d2586..bd477f8068ff 100644 --- a/packages/SettingsLib/res/layout/preference_two_target_divider.xml +++ b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2017 The Android Open Source Project + Copyright (C) 2021 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SettingsLib/TwoTargetPreference/res/values/dimens.xml b/packages/SettingsLib/TwoTargetPreference/res/values/dimens.xml new file mode 100644 index 000000000000..32a865905267 --- /dev/null +++ b/packages/SettingsLib/TwoTargetPreference/res/values/dimens.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License + --> +<resources> + + <dimen name="two_target_pref_small_icon_size">24dp</dimen> + <dimen name="two_target_pref_medium_icon_size">32dp</dimen> +</resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java index 02895a479352..9130662021d5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java +++ b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2021 The Android Open 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,21 +14,24 @@ * limitations under the License. */ -package com.android.settingslib; +package com.android.settingslib.widget; -import android.annotation.IntDef; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; +import androidx.annotation.IntDef; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +/** + * The Base preference with two target areas divided by a vertical divider + */ public class TwoTargetPreference extends Preference { @IntDef({ICON_SIZE_DEFAULT, ICON_SIZE_MEDIUM, ICON_SIZE_SMALL}) diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index ef4b97f7743f..9d5b23166190 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -32,9 +32,6 @@ <dimen name="user_spinner_padding_sides">20dp</dimen> <dimen name="user_spinner_item_height">56dp</dimen> - <dimen name="two_target_pref_small_icon_size">24dp</dimen> - <dimen name="two_target_pref_medium_icon_size">32dp</dimen> - <!-- Lock icon for preferences locked by admin --> <dimen name="restricted_icon_padding">4dp</dimen> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java index ad7e995412aa..fc8b5879c5fa 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java @@ -27,6 +27,8 @@ import androidx.core.content.res.TypedArrayUtils; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceViewHolder; +import com.android.settingslib.widget.TwoTargetPreference; + /** * Preference class that supports being disabled by a user restriction * set by a device admin. diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 9c0c80bff3df..bf4242f542da 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -23,7 +23,6 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.location.LocationManager; import android.media.AudioManager; -import android.net.ConnectivityManager; import android.net.NetworkCapabilities; import android.net.TetheringManager; import android.net.vcn.VcnTransportInfo; @@ -37,6 +36,7 @@ import android.provider.Settings; import android.telephony.AccessNetworkConstants; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; +import android.telephony.TelephonyManager; import androidx.annotation.NonNull; import androidx.core.graphics.drawable.RoundedBitmapDrawable; @@ -439,8 +439,7 @@ public class Utils { } public static boolean isWifiOnly(Context context) { - return !context.getSystemService(ConnectivityManager.class) - .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + return !context.getSystemService(TelephonyManager.class).isDataCapable(); } /** Returns if the automatic storage management feature is turned on or not. **/ diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java index 092cbf3c7c12..60bcf37304a5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java @@ -16,7 +16,6 @@ package com.android.settingslib.net; -import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; @@ -59,7 +58,6 @@ public class DataUsageController { PERIOD_BUILDER, Locale.getDefault()); private final Context mContext; - private final ConnectivityManager mConnectivityManager; private final INetworkStatsService mStatsService; private final NetworkPolicyManager mPolicyManager; private final NetworkStatsManager mNetworkStatsManager; @@ -71,7 +69,6 @@ public class DataUsageController { public DataUsageController(Context context) { mContext = context; - mConnectivityManager = ConnectivityManager.from(context); mStatsService = INetworkStatsService.Stub.asInterface( ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); mPolicyManager = NetworkPolicyManager.from(mContext); @@ -236,7 +233,7 @@ public class DataUsageController { public boolean isMobileDataSupported() { // require both supported network and ready SIM - return mConnectivityManager.isNetworkSupported(TYPE_MOBILE) + return getTelephonyManager().isDataCapable() && getTelephonyManager().getSimState() == SIM_STATE_READY; } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java index 3f0ba13ce50a..aaec909aa335 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2021 The Android Open 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,11 +14,11 @@ * limitations under the License. */ -package com.android.settingslib; +package com.android.settingslib.widget; -import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_DEFAULT; -import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM; -import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_SMALL; +import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_DEFAULT; +import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM; +import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_SMALL; import static com.google.common.truth.Truth.assertThat; @@ -32,6 +32,8 @@ import android.widget.LinearLayout; import androidx.preference.PreferenceViewHolder; +import com.android.settingslib.R; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 6568bffddecc..268603fa8b0d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -2445,8 +2445,8 @@ class DatabaseHelper extends SQLiteOpenHelper { R.bool.def_auto_time_zone); // Sync timezone to NITZ loadSetting(stmt, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, - ("1".equals(SystemProperties.get("ro.kernel.qemu")) || - res.getBoolean(R.bool.def_stay_on_while_plugged_in)) + ("1".equals(SystemProperties.get("ro.boot.qemu")) + || res.getBoolean(R.bool.def_stay_on_while_plugged_in)) ? 1 : 0); loadIntegerSetting(stmt, Settings.Global.WIFI_SLEEP_POLICY, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index b4194fd5bbf9..cf66bad45f3b 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -187,6 +187,7 @@ <uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" /> <uses-permission android:name="android.permission.MANAGE_SENSORS" /> <uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" /> + <uses-permission android:name="android.permission.QUERY_AUDIO_STATE" /> <uses-permission android:name="android.permission.MANAGE_CAMERA" /> <!-- Permissions needed to test system only camera devices --> <uses-permission android:name="android.permission.CAMERA" /> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java index 0d348e263545..1fde6c9df90a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java @@ -95,4 +95,15 @@ public interface FalsingManager { /** Call to report a ProximityEvent to the FalsingManager. */ void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent); + + /** Adds a {@link FalsingBeliefListener}. */ + void addFalsingBeliefListener(FalsingBeliefListener listener); + + /** Removes a {@link FalsingBeliefListener}. */ + void removeFalsingBeliefListener(FalsingBeliefListener listener); + + /** Listener that is alerted when falsing belief level crosses a predfined threshold. */ + interface FalsingBeliefListener { + void onFalse(); + } } diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml index 6ae759c66bea..50ffbc802a7d 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml @@ -59,124 +59,127 @@ </com.android.keyguard.AlphaOptimizedRelativeLayout> <LinearLayout android:id="@+id/row1" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key1" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="1" /> <com.android.keyguard.NumPadKey android:id="@+id/key2" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="2" /> <com.android.keyguard.NumPadKey android:id="@+id/key3" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pinEntry" androidprv:digit="3" /> </LinearLayout> <LinearLayout android:id="@+id/row2" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key4" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="4" /> <com.android.keyguard.NumPadKey android:id="@+id/key5" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="5" /> <com.android.keyguard.NumPadKey android:id="@+id/key6" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pinEntry" androidprv:digit="6" /> </LinearLayout> <LinearLayout android:id="@+id/row3" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key7" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="7" /> <com.android.keyguard.NumPadKey android:id="@+id/key8" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="8" /> <com.android.keyguard.NumPadKey android:id="@+id/key9" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pinEntry" androidprv:digit="9" /> </LinearLayout> <LinearLayout android:id="@+id/row4" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" > <com.android.keyguard.NumPadButton android:id="@+id/delete_button" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" android:contentDescription="@string/keyboardview_keycode_delete" style="@style/NumPadKey.Delete" /> <com.android.keyguard.NumPadKey android:id="@+id/key0" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="0" /> <com.android.keyguard.NumPadButton android:id="@+id/key_enter" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" style="@style/NumPadKey.Enter" android:contentDescription="@string/keyboardview_keycode_enter" /> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml index f709424a7d12..297c20ef8d6d 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml @@ -71,121 +71,124 @@ /> </RelativeLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key1" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="1" /> <com.android.keyguard.NumPadKey android:id="@+id/key2" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="2" /> <com.android.keyguard.NumPadKey android:id="@+id/key3" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/simPinEntry" androidprv:digit="3" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key4" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="4" /> <com.android.keyguard.NumPadKey android:id="@+id/key5" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="5" /> <com.android.keyguard.NumPadKey android:id="@+id/key6" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/simPinEntry" androidprv:digit="6" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key7" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="7" /> <com.android.keyguard.NumPadKey android:id="@+id/key8" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="8" /> <com.android.keyguard.NumPadKey android:id="@+id/key9" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/simPinEntry" androidprv:digit="9" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" > <com.android.keyguard.NumPadButton android:id="@+id/delete_button" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" android:contentDescription="@string/keyboardview_keycode_delete" style="@style/NumPadKey.Delete" /> <com.android.keyguard.NumPadKey android:id="@+id/key0" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="0" /> <com.android.keyguard.NumPadButton android:id="@+id/key_enter" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" style="@style/NumPadKey.Enter" android:contentDescription="@string/keyboardview_keycode_enter" /> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index 2f9fed610f1d..388919e6d81c 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -72,121 +72,125 @@ /> </RelativeLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key1" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="1" /> <com.android.keyguard.NumPadKey android:id="@+id/key2" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="2" /> <com.android.keyguard.NumPadKey android:id="@+id/key3" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pukEntry" androidprv:digit="3" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" + > <com.android.keyguard.NumPadKey android:id="@+id/key4" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="4" /> <com.android.keyguard.NumPadKey android:id="@+id/key5" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="5" /> <com.android.keyguard.NumPadKey android:id="@+id/key6" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pukEntry" androidprv:digit="6" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key7" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="7" /> <com.android.keyguard.NumPadKey android:id="@+id/key8" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="8" /> <com.android.keyguard.NumPadKey android:id="@+id/key9" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pukEntry" androidprv:digit="9" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" > <com.android.keyguard.NumPadButton android:id="@+id/delete_button" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" android:contentDescription="@string/keyboardview_keycode_delete" style="@style/NumPadKey.Delete" /> <com.android.keyguard.NumPadKey android:id="@+id/key0" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="0" /> <com.android.keyguard.NumPadButton android:id="@+id/key_enter" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" style="@style/NumPadKey.Enter" android:contentDescription="@string/keyboardview_keycode_enter" /> diff --git a/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml new file mode 100644 index 000000000000..be39030451f4 --- /dev/null +++ b/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2021, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Spacing around each button used for PIN view --> + <dimen name="num_pad_key_width">84dp</dimen> + <dimen name="num_pad_row_margin_bottom">12dp</dimen> + <dimen name="num_pad_key_margin_end">20dp</dimen> +</resources> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 115a156b65e7..07bd2e6f0505 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -38,7 +38,7 @@ <!-- Margin around the various security views --> <dimen name="keyguard_security_view_top_margin">8dp</dimen> - <dimen name="keyguard_security_view_lateral_margin">36dp</dimen> + <dimen name="keyguard_security_view_lateral_margin">20dp</dimen> <dimen name="keyguard_eca_top_margin">18dp</dimen> @@ -87,5 +87,7 @@ <dimen name="disappear_y_translation">-32dp</dimen> <!-- Spacing around each button used for PIN view --> - <dimen name="num_pad_key_margin">2dp</dimen> + <dimen name="num_pad_key_width">72dp</dimen> + <dimen name="num_pad_row_margin_bottom">6dp</dimen> + <dimen name="num_pad_key_margin_end">12dp</dimen> </resources> diff --git a/packages/SystemUI/res/color/remote_input_send.xml b/packages/SystemUI/res/color/remote_input_send.xml index fe2ffaa838eb..bd91ef9785d1 100644 --- a/packages/SystemUI/res/color/remote_input_send.xml +++ b/packages/SystemUI/res/color/remote_input_send.xml @@ -16,6 +16,6 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_enabled="true" android:color="@android:color/white" /> - <item android:color="#4dffffff" /> <!-- 30% white --> + <item android:state_enabled="true" android:color="?android:attr/colorAccent" /> + <item android:color="?android:attr/colorAccent" android:alpha=".3" /> </selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/color/remote_input_text.xml b/packages/SystemUI/res/color/remote_input_text.xml index 8e18e16f8abc..33eeb7794d09 100644 --- a/packages/SystemUI/res/color/remote_input_text.xml +++ b/packages/SystemUI/res/color/remote_input_text.xml @@ -16,6 +16,6 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_enabled="true" android:color="@color/remote_input_text_enabled" /> <!-- white --> - <item android:color="#99ffffff" /> <!-- 60% white --> + <item android:state_enabled="true" android:color="?android:attr/textColorTertiary" /> + <item android:color="?android:attr/textColorTertiary" android:alpha=".6" /> </selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/fingerprint_bg.xml b/packages/SystemUI/res/drawable/fingerprint_bg.xml new file mode 100644 index 000000000000..2b0ab6f9a8d2 --- /dev/null +++ b/packages/SystemUI/res/drawable/fingerprint_bg.xml @@ -0,0 +1,25 @@ +<!-- Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + + <solid + android:color="?android:attr/colorBackground"/> + + <size + android:width="64dp" + android:height="64dp"/> +</shape> diff --git a/packages/SystemUI/res/drawable/ic_message.xml b/packages/SystemUI/res/drawable/ic_message.xml new file mode 100644 index 000000000000..8219eeef4be9 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_message.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" + android:viewportWidth="48" + android:viewportHeight="48" + android:width="48dp" + android:height="48dp"> + <path + android:pathData="M40 4H8C5.79 4 4.02 5.79 4.02 8L4 44l8 -8h28c2.21 0 4 -1.79 4 -4V8c0 -2.21 -1.79 -4 -4 -4zM12 18h24v4H12v-4zm16 10H12v-4h16v4zm8 -12H12v-4h24v4z" + android:fillColor="#FF000000" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_photo_camera.xml b/packages/SystemUI/res/drawable/ic_photo_camera.xml new file mode 100644 index 000000000000..63cd4e2e5cc4 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_photo_camera.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/> + <path + android:fillColor="#FF000000" + android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml b/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml index b1c13287d203..b1a37bf71beb 100644 --- a/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml +++ b/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml @@ -28,48 +28,27 @@ <LinearLayout android:orientation="horizontal" android:gravity="center" + android:layout_gravity="center" android:paddingVertical="2dp" android:paddingHorizontal="8dp" android:layout_width="match_parent" android:layout_height="match_parent"> - <LinearLayout - android:background="@drawable/people_space_new_story_outline" - android:id="@+id/person_icon_with_story" - android:gravity="center_horizontal" - android:layout_width="60dp" - android:layout_height="60dp"> - <ImageView - android:id="@+id/person_icon_inside_ring" - android:layout_marginEnd="4dp" - android:layout_marginStart="4dp" - android:layout_marginBottom="4dp" - android:layout_marginTop="4dp" - android:layout_width="52dp" - android:layout_height="52dp"/> - </LinearLayout> <ImageView - android:id="@+id/person_icon_only" + android:id="@+id/person_icon" android:layout_width="60dp" android:layout_height="60dp"/> - <ImageView - android:id="@+id/package_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_marginStart="-20dp" - android:layout_marginTop="22dp"/> - + android:id="@+id/availability" + android:layout_width="10dp" + android:layout_height="10dp" + android:background="@drawable/circle_green_10dp"/> <LinearLayout android:orientation="vertical" - android:paddingStart="8dp" + android:paddingStart="4dp" + android:gravity="top" android:layout_width="match_parent" android:layout_height="wrap_content"> - <ImageView - android:id="@+id/availability" - android:layout_width="10dp" - android:layout_height="10dp" - android:background="@drawable/circle_green_10dp"/> <TextView android:id="@+id/name" android:text="@string/empty_user_name" diff --git a/packages/SystemUI/res/layout/people_space_notification_content_tile.xml b/packages/SystemUI/res/layout/people_space_notification_content_tile.xml deleted file mode 100644 index 9ea7aa3c94e5..000000000000 --- a/packages/SystemUI/res/layout/people_space_notification_content_tile.xml +++ /dev/null @@ -1,154 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> - <RelativeLayout - android:background="@drawable/people_space_tile_view_card" - android:id="@+id/item" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <include layout="@layout/punctuation_layout"/> - <RelativeLayout - android:gravity="start" - android:id="@+id/column_one" - android:paddingVertical="10dp" - android:paddingStart="8dp" - android:layout_width="wrap_content" - android:layout_height="match_parent"> - <TextView - android:id="@+id/subtext" - android:layout_toStartOf="@+id/content_layout" - android:layout_alignParentStart="true" - android:layout_alignParentTop="true" - android:gravity="top|start" - android:textColor="?android:attr/textColorSecondary" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textSize="12sp" - android:maxWidth="60dp" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLines="1" - android:ellipsize="end"/> - <LinearLayout - android:orientation="horizontal" - android:id="@+id/avatar_and_app_icon" - android:layout_alignParentBottom="true" - android:layout_alignParentStart="true" - android:gravity="center" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - <LinearLayout - android:id="@+id/person_icon_with_story" - android:background="@drawable/people_space_new_story_outline" - android:gravity="center_horizontal" - android:layout_width="48dp" - android:layout_height="48dp"> - <ImageView - android:id="@+id/person_icon_inside_ring" - android:layout_marginEnd="4dp" - android:layout_marginStart="4dp" - android:layout_marginBottom="4dp" - android:layout_marginTop="4dp" - android:layout_width="40dp" - android:layout_height="40dp"/> - </LinearLayout> - <ImageView - android:id="@+id/person_icon_only" - android:layout_width="48dp" - android:layout_height="48dp"/> - <ImageView - android:id="@id/package_icon" - android:layout_marginStart="-16dp" - android:layout_marginTop="18dp" - android:paddingBottom="10dp" - android:paddingEnd="8dp" - android:layout_width="28dp" - android:layout_height="32dp"/> - </LinearLayout> - </RelativeLayout> - <LinearLayout - android:id="@+id/content_layout" - android:paddingBottom="4dp" - android:paddingStart="4dp" - android:paddingTop="8dp" - android:paddingEnd="8dp" - android:layout_alignParentEnd="true" - android:layout_alignParentTop="true" - android:layout_toEndOf="@id/column_one" - android:gravity="top|end" - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - <TextView - android:id="@+id/content" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" - android:gravity="top|end" - android:textSize="12sp" - android:paddingTop="2dp" - android:paddingEnd="4dp" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:maxLines="2" - android:ellipsize="end"/> - <LinearLayout - android:id="@+id/content_background" - android:background="@drawable/people_space_content_background" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <ImageView - android:id="@+id/image" - android:adjustViewBounds="true" - android:maxHeight="44dp" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:scaleType="centerCrop"/> - </LinearLayout> - </LinearLayout> - <LinearLayout - android:id="@+id/person_label" - android:paddingBottom="10dp" - android:paddingEnd="8dp" - android:gravity="start|bottom" - android:layout_toEndOf="@id/column_one" - android:layout_alignParentBottom="true" - android:layout_alignParentEnd="true" - android:layout_below="@id/content_layout" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <ImageView - android:id="@+id/availability" - android:layout_width="10dp" - android:layout_height="10dp" - android:paddingVertical="2dp" - android:background="@drawable/circle_green_10dp"/> - <TextView - android:id="@+id/name" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" - android:textSize="14sp" - android:maxLines="1" - android:ellipsize="end" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - </LinearLayout> - </RelativeLayout> -</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml b/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml index 33004952d947..6a606e10e3fb 100644 --- a/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml +++ b/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml @@ -1,44 +1,69 @@ <?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. - --> + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> - <RelativeLayout + <LinearLayout android:background="@drawable/people_space_tile_view_card" android:id="@+id/item" + android:orientation="vertical" + android:layout_gravity="center" + android:padding="8dp" android:layout_width="match_parent" - android:layout_height="match_parent"> - <RelativeLayout - android:gravity="start" - android:id="@+id/column_one" - android:paddingVertical="8dp" - android:paddingStart="8dp" - android:layout_width="wrap_content" - android:layout_height="match_parent"> + android:layout_height="wrap_content"> + <LinearLayout + android:orientation="horizontal" + android:gravity="top" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <ImageView + android:gravity="start" + android:id="@+id/person_icon" + android:layout_width="56dp" + android:layout_height="56dp"/> + <ImageView + android:id="@+id/availability" + android:layout_width="10dp" + android:layout_height="10dp" + android:background="@drawable/circle_green_10dp"/> + <LinearLayout + android:orientation="vertical" + android:gravity="top|start" + android:paddingStart="12dp" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:id="@+id/subtext" + android:text="@string/empty_user_name" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" + android:textColor="?android:attr/textColorSecondary" + android:textSize="12sp" + android:paddingBottom="4dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ellipsize="end"/> <LinearLayout - android:orientation="horizontal" android:id="@+id/content_background" android:background="@drawable/people_space_content_background" - android:layout_alignParentStart="true" - android:layout_alignParentTop="true" - android:layout_width="60dp" - android:layout_height="60dp"> + android:layout_width="match_parent" + android:layout_height="match_parent"> <ImageView android:id="@+id/image" android:gravity="center" @@ -46,105 +71,33 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="centerCrop"/> - <ImageView - android:id="@+id/status_defined_icon" - android:gravity="start|top" - android:layout_marginStart="-52dp" - android:layout_marginTop="8dp" - android:layout_width="18dp" - android:layout_height="18dp"/> </LinearLayout> - <LinearLayout - android:orientation="horizontal" - android:id="@+id/avatar_and_app_icon" - android:layout_alignParentBottom="true" - android:layout_alignParentStart="true" - android:paddingStart="4dp" - android:gravity="center" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - <LinearLayout - android:id="@+id/person_icon_with_story" - android:background="@drawable/people_space_new_story_outline" - android:gravity="center_horizontal" - android:layout_width="48dp" - android:layout_height="48dp"> - <ImageView - android:id="@+id/person_icon_inside_ring" - android:layout_marginEnd="4dp" - android:layout_marginStart="4dp" - android:layout_marginBottom="4dp" - android:layout_marginTop="4dp" - android:layout_width="40dp" - android:layout_height="40dp"/> - </LinearLayout> - <ImageView - android:id="@+id/person_icon_only" - android:layout_width="48dp" - android:layout_height="48dp"/> - <ImageView - android:id="@id/package_icon" - android:layout_marginStart="-16dp" - android:layout_marginTop="18dp" - android:paddingBottom="10dp" - android:paddingEnd="8dp" - android:layout_width="28dp" - android:layout_height="32dp"/> - </LinearLayout> - </RelativeLayout> - <LinearLayout - android:id="@+id/content_layout" - android:paddingTop="10dp" - android:paddingBottom="4dp" - android:paddingHorizontal="8dp" - android:layout_alignParentEnd="true" - android:layout_alignParentTop="true" - android:layout_toEndOf="@id/column_one" - android:gravity="top|end" - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> <TextView - android:id="@+id/status" + android:id="@+id/text_content" + android:text="@string/empty_status" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" android:textColor="?android:attr/textColorPrimary" android:textSize="12sp" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:maxLines="2" + android:singleLine="false" android:ellipsize="end"/> + </LinearLayout> </LinearLayout> <LinearLayout - android:id="@+id/person_label" - android:paddingBottom="10dp" - android:paddingEnd="8dp" - android:gravity="start|bottom" - android:layout_toEndOf="@id/column_one" - android:layout_alignParentBottom="true" - android:layout_alignParentEnd="true" - android:layout_below="@id/content_layout" - android:orientation="vertical" + android:gravity="bottom" + android:orientation="horizontal" + android:paddingTop="4dp" + android:layout_weight="1" android:layout_width="match_parent" android:layout_height="wrap_content"> - <TextView - android:id="@+id/time" - android:textColor="?android:attr/textColorSecondary" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textSize="12sp" - android:paddingVertical="2dp" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLines="1" - android:visibility="gone" - android:ellipsize="end"/> - <ImageView - android:id="@+id/availability" - android:layout_width="10dp" - android:layout_height="10dp" - android:paddingVertical="2dp" - android:background="@drawable/circle_green_10dp"/> + <TextView android:id="@+id/name" + android:gravity="center_vertical" + android:layout_weight="1" + android:text="@string/empty_user_name" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" android:textColor="?android:attr/textColorPrimary" android:textSize="14sp" @@ -152,6 +105,13 @@ android:ellipsize="end" android:layout_width="wrap_content" android:layout_height="wrap_content"/> + <ImageView + android:id="@+id/predefined_icon" + android:gravity="end" + android:paddingStart="6dp" + android:layout_width="24dp" + android:layout_height="18dp"/> </LinearLayout> - </RelativeLayout> -</LinearLayout>
\ No newline at end of file + </LinearLayout> +</LinearLayout> + diff --git a/packages/SystemUI/res/layout/people_space_small_view.xml b/packages/SystemUI/res/layout/people_space_small_view.xml new file mode 100644 index 000000000000..7b1abaed28ca --- /dev/null +++ b/packages/SystemUI/res/layout/people_space_small_view.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:id="@+id/item" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:background="@drawable/people_space_tile_view_card" + android:orientation="vertical" + android:paddingHorizontal="4dp" + android:paddingVertical="8dp"> + + <ImageView + android:id="@+id/person_icon" + android:layout_gravity="center" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" /> + + <ImageView + android:id="@+id/predefined_icon" + android:layout_gravity="center" + android:paddingTop="4dp" + android:layout_width="18dp" + android:layout_height="22dp" + android:layout_weight="1" /> + + <TextView + android:id="@+id/name" + android:layout_gravity="center" + android:paddingTop="4dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:ellipsize="end" + android:maxLines="1" + android:paddingHorizontal="8dp" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" + android:textColor="?android:attr/textColorPrimary" + android:textSize="14sp" /> + </LinearLayout> +</FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml index b969a1527c09..4798b437298a 100644 --- a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml @@ -38,7 +38,6 @@ android:id="@+id/device_management_subtitle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/monitoring_title_device_owned" style="@style/DeviceManagementDialogTitle" android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding" /> diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml index b5d48b4636a8..43182eb991e5 100644 --- a/packages/SystemUI/res/layout/remote_input.xml +++ b/packages/SystemUI/res/layout/remote_input.xml @@ -19,7 +19,6 @@ <!-- LinearLayout --> <com.android.systemui.statusbar.policy.RemoteInputView xmlns:android="http://schemas.android.com/apk/res/android" - android:theme="@style/systemui_theme_remote_input" android:id="@+id/remote_input" android:layout_height="match_parent" android:layout_width="match_parent"> @@ -33,6 +32,10 @@ android:paddingBottom="4dp" android:paddingStart="16dp" android:paddingEnd="12dp" + android:layout_marginRight="5dp" + android:layout_marginLeft="20dp" + android:layout_marginTop="5dp" + android:layout_marginBottom="20dp" android:gravity="start|center_vertical" android:textAppearance="?android:attr/textAppearance" android:textColor="@color/remote_input_text" @@ -53,6 +56,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center" + android:paddingBottom="20dp" android:paddingStart="12dp" android:paddingEnd="24dp" android:id="@+id/remote_input_send" @@ -66,6 +70,7 @@ android:id="@+id/remote_input_progress" android:layout_width="24dp" android:layout_height="24dp" + android:layout_marginBottom="10dp" android:layout_marginEnd="6dp" android:layout_gravity="center" android:visibility="invisible" diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml index 0199ccb04be6..562040b0ad82 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_view.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml @@ -20,7 +20,13 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - <!-- TODO: add background protection --> + <!-- Background protection --> + <ImageView + android:id="@+id/udfps_keyguard_fp_bg" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/fingerprint_bg" + android:visibility="gone"/> <!-- Fingerprint --> <ImageView diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 424172458b80..d4783197e1f1 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -153,10 +153,9 @@ <color name="minimize_dock_shadow_end">#00000000</color> <color name="default_remote_input_background">@*android:color/notification_default_color</color> - <color name="remote_input_text_enabled">#ffffffff</color> <color name="remote_input_hint">#99ffffff</color> - <color name="remote_input_accent">#eeeeee</color> + <color name="remote_input_accent">?android:attr/colorAccent</color> <color name="quick_step_track_background_background_dark">#1F000000</color> <color name="quick_step_track_background_background_light">#33FFFFFF</color> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index a8083f164576..57c932eaa8e4 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1257,6 +1257,9 @@ <!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the organization can monitor network traffic on that device. The placeholder is the organization's name. [CHAR LIMIT=100] --> <string name="quick_settings_disclosure_named_management_monitoring"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> owns this device and may monitor network traffic</string> + <!-- Disclosure at the bottom of Quick Settings that indicates that the user's financed device belongs to the Creditor. The placeholder is the Creditor's name. [CHAR LIMIT=100] --> + <string name="quick_settings_financed_disclosure_named_management">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string> + <!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] --> <string name="quick_settings_disclosure_management_named_vpn">This device belongs to your organization and is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string> @@ -1296,6 +1299,9 @@ <!-- Disclosure at the bottom of Quick Settings that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] --> <string name="quick_settings_disclosure_named_vpn">This device is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string> + <!-- Monitoring dialog title for financed device [CHAR LIMIT=60] --> + <string name="monitoring_title_financed_device">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string> + <!-- Monitoring dialog title for device owned devices [CHAR LIMIT=35] --> <string name="monitoring_title_device_owned">Device management</string> @@ -1330,6 +1336,9 @@ <!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]--> <string name="monitoring_description_named_management">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string> + <!-- Dialog that a user can access via Quick Settings. The dialog describes what a Creditor can monitor (and the changes they can make) on the user's financed device. [CHAR LIMIT=NONE]--> + <string name="monitoring_financed_description_named_management"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may be able to access data associated with this device and change this device\’s settings.\n\nIf you have questions, contact <xliff:g id="organization_name" example="Foo, Inc.">%2$s</xliff:g>.</string> + <!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]--> <string name="monitoring_description_management">This device belongs to your organization.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string> @@ -2295,6 +2304,9 @@ <!-- accessibility label for button to select user [CHAR LIMIT=NONE] --> <string name="accessibility_quick_settings_user">Signed in as <xliff:g name="user" example="John">%s</xliff:g></string> + <!-- Accessibility description (not shown on the screen) of action to open user switcher when the multi-user icon is clicked. It will read as "Double-tap to choose user" in screen readers [CHAR LIMIT=NONE] --> + <string name="accessibility_quick_settings_choose_user_action">choose user</string> + <!-- Content description for no internet connection [CHAR LIMIT=NONE] --> <string name="data_connection_no_internet">No internet</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index fb885cb3fdbe..ff9ea0175ec0 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -384,10 +384,6 @@ <!-- Overridden by values-television/styles.xml with tv-specific settings --> <style name="volume_dialog_theme" parent="qs_theme"/> - <style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light"> - <item name="android:colorAccent">@color/remote_input_accent</item> - </style> - <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog" /> <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" /> diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml index 35188d8e11ad..d2bff180ef8a 100644 --- a/packages/SystemUI/res/xml/people_space_widget_info.xml +++ b/packages/SystemUI/res/xml/people_space_widget_info.xml @@ -15,10 +15,11 @@ --> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" - android:minWidth="140dp" - android:minHeight="55dp" - android:minResizeWidth="110dp" - android:minResizeHeight="55dp" + android:minWidth="120dp" + android:minHeight="54dp" + android:minResizeWidth="60dp" + android:minResizeHeight="54dp" + android:maxResizeHeight="207dp" android:updatePeriodMillis="60000" android:description="@string/people_tile_description" android:previewLayout="@layout/people_space_placeholder_layout" diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index f98a959346d3..09e9675a3277 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -41,7 +41,6 @@ android_library { srcs: [ "src/**/*.java", "src/**/I*.aidl", - ":wm_shell-aidls", ], static_libs: [ diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java index 72e4061829fa..f50c3c925a1c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java @@ -20,7 +20,6 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.view.Choreographer; -import android.view.Surface; import android.view.SurfaceControl; /** @@ -91,24 +90,6 @@ public class PipSurfaceTransactionHelper { .setPosition(leash, positionX, positionY); } - public void reset(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds, - @Surface.Rotation int rotation) { - resetScale(tx, leash, destinationBounds); - if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { - final int degree = (rotation == Surface.ROTATION_90) ? -90 : 90; - mTmpTransform.setRotate(degree, 0, 0); - tx.setMatrix(leash, mTmpTransform, mTmpFloat9); - } - resetCornerRadius(tx, leash); - crop(tx, leash, destinationBounds); - } - - public void resetScale(SurfaceControl.Transaction tx, SurfaceControl leash, - Rect destinationBounds) { - tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, mTmpFloat9) - .setPosition(leash, destinationBounds.left, destinationBounds.top); - } - public void resetCornerRadius(SurfaceControl.Transaction tx, SurfaceControl leash) { tx.setCornerRadius(leash, 0); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl index 2569b780c1bb..97aa512ea7df 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl @@ -14,14 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.pip; +package com.android.systemui.shared.recents; /** - * Listener interface that Launcher attaches to SystemUI to get Pip animation callbacks. + * Listener interface that Launcher attaches to SystemUI to get + * pinned stack animation callbacks. */ -oneway interface IPipAnimationListener { +oneway interface IPinnedStackAnimationListener { /** - * Notifies the listener that the Pip animation is started. + * Notifies the pinned stack animation is started. */ - void onPipAnimationStarted(); + void onPinnedStackAnimationStarted(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreenListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl index faab4c2009cf..54242bece2b6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreenListener.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl @@ -14,20 +14,12 @@ * limitations under the License. */ -package com.android.wm.shell.splitscreen; +package com.android.systemui.shared.recents; /** * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks. */ oneway interface ISplitScreenListener { - - /** - * Called when the stage position changes. - */ void onStagePositionChanged(int stage, int position); - - /** - * Called when a task changes stages. - */ void onTaskStageChanged(int taskId, int stage, boolean visible); -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindowListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IStartingWindowListener.aidl index f562c8fc4f85..eb3e60cec5c5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindowListener.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IStartingWindowListener.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.startingsurface; +package com.android.systemui.shared.recents; /** * Listener interface that Launcher attaches to SystemUI to get diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index 3da30856ff64..512628447530 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -16,6 +16,11 @@ package com.android.systemui.shared.recents; +import android.app.PendingIntent; +import android.app.PictureInPictureParams; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; @@ -23,15 +28,26 @@ import android.os.Bundle; import android.os.UserHandle; import android.view.MotionEvent; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; +import com.android.systemui.shared.recents.ISplitScreenListener; +import com.android.systemui.shared.recents.IStartingWindowListener; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.RemoteTransitionCompat; /** * Temporary callbacks into SystemUI. + * Next id = 44 */ interface ISystemUiProxy { /** + * Proxies SurfaceControl.screenshotToBuffer(). + * @Removed + * GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer, + * int maxLayer, boolean useIdentityTransform, int rotation) = 0; + */ + + /** * Begins screen pinning on the provided {@param taskId}. */ void startScreenPinning(int taskId) = 1; @@ -99,6 +115,11 @@ interface ISystemUiProxy { void stopScreenPinning() = 17; /** + * Sets the shelf height and visibility. + */ + void setShelfHeight(boolean visible, int shelfHeight) = 20; + + /** * Handle the provided image as if it was a screenshot. * * Deprecated, use handleImageBundleAsScreenshot with image bundle and UserTask @@ -118,12 +139,27 @@ interface ISystemUiProxy { void notifySwipeToHomeFinished() = 23; /** + * Sets listener to get pinned stack animation callbacks. + */ + void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) = 24; + + /** * Notifies that quickstep will switch to a new task * @param rotation indicates which Surface.Rotation the gesture was started in */ void onQuickSwitchToNewTask(int rotation) = 25; /** + * Start the one-handed mode. + */ + void startOneHandedMode() = 26; + + /** + * Stop the one-handed mode. + */ + void stopOneHandedMode() = 27; + + /** * Handle the provided image as if it was a screenshot. */ void handleImageBundleAsScreenshot(in Bundle screenImageBundle, in Rect locationInScreen, @@ -134,5 +170,88 @@ interface ISystemUiProxy { */ void expandNotificationPanel() = 29; - // Next id = 44 + /** + * Notifies that Activity is about to be swiped to home with entering PiP transition and + * queries the destination bounds for PiP depends on Launcher's rotation and shelf height. + * + * @param componentName ComponentName represents the Activity + * @param activityInfo ActivityInfo tied to the Activity + * @param pictureInPictureParams PictureInPictureParams tied to the Activity + * @param launcherRotation Launcher rotation to calculate the PiP destination bounds + * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds + * @return destination bounds the PiP window should land into + */ + Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo, + in PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) = 30; + + /** + * Notifies the swiping Activity to PiP onto home transition is finished + * + * @param componentName ComponentName represents the Activity + * @param destinationBounds the destination bounds the PiP window lands into + */ + void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 31; + + /** + * Registers a RemoteTransitionCompat that will handle transitions. This parameter bundles an + * IRemoteTransition and a filter that must pass for it. + */ + void registerRemoteTransition(in RemoteTransitionCompat remoteTransition) = 32; + + /** Unegisters a RemoteTransitionCompat that will handle transitions. */ + void unregisterRemoteTransition(in RemoteTransitionCompat remoteTransition) = 33; + +// SplitScreen APIs...copied from SplitScreen.java + /** + * Stage position isn't specified normally meaning to use what ever it is currently set to. + */ + //int STAGE_POSITION_UNDEFINED = -1; + /** + * Specifies that a stage is positioned at the top half of the screen if + * in portrait mode or at the left half of the screen if in landscape mode. + */ + //int STAGE_POSITION_TOP_OR_LEFT = 0; + /** + * Specifies that a stage is positioned at the bottom half of the screen if + * in portrait mode or at the right half of the screen if in landscape mode. + */ + //int STAGE_POSITION_BOTTOM_OR_RIGHT = 1; + + /** + * Stage type isn't specified normally meaning to use what ever the default is. + * E.g. exit split-screen and launch the app in fullscreen. + */ + //int STAGE_TYPE_UNDEFINED = -1; + /** + * The main stage type. + * @see MainStage + */ + //int STAGE_TYPE_MAIN = 0; + /** + * The side stage type. + * @see SideStage + */ + //int STAGE_TYPE_SIDE = 1; + + void registerSplitScreenListener(in ISplitScreenListener listener) = 34; + void unregisterSplitScreenListener(in ISplitScreenListener listener) = 35; + + /** Hides the side-stage if it is currently visible. */ + void setSideStageVisibility(in boolean visible) = 36; + /** Removes the split-screen stages. */ + void exitSplitScreen() = 37; + /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */ + void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 38; + void startTask(in int taskId, in int stage, in int position, in Bundle options) = 39; + void startShortcut(in String packageName, in String shortcutId, in int stage, in int position, + in Bundle options, in UserHandle user) = 40; + void startIntent( + in PendingIntent intent, in Intent fillInIntent, in int stage, in int position, + in Bundle options) = 41; + void removeFromSideStage(in int taskId) = 42; + /** + * Sets listener to get task launching callbacks. + */ + void setStartingWindowListener(IStartingWindowListener listener) = 43; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 41840afc4995..937c1df10315 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -41,18 +41,6 @@ public class QuickStepContract { public static final String KEY_EXTRA_INPUT_MONITOR = "extra_input_monitor"; public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners"; - // See IPip.aidl - public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip"; - // See ISplitScreen.aidl - public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen"; - // See IOneHanded.aidl - public static final String KEY_EXTRA_SHELL_ONE_HANDED = "extra_shell_one_handed"; - // See IShellTransitions.aidl - public static final String KEY_EXTRA_SHELL_SHELL_TRANSITIONS = - "extra_shell_shell_transitions"; - // See IStartingWindow.aidl - public static final String KEY_EXTRA_SHELL_STARTING_WINDOW = - "extra_shell_starting_window"; public static final String NAV_BAR_MODE_2BUTTON_OVERLAY = WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index c2d52a7855f4..bf0d29a59bea 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -76,10 +76,14 @@ public class RecentsAnimationControllerCompat { * accordingly. This should be called before `finish` * @param taskId Task id of the Activity in PiP mode. * @param destinationBounds Bounds of the PiP window on home. + * @param windowCrop bounds to crop as part of final transform. + * @param float9 An array of 9 floats to be used as matrix transform. */ - public void setFinishTaskBounds(int taskId, Rect destinationBounds) { + public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop, + float[] float9) { try { - mAnimationController.setFinishTaskBounds(taskId, destinationBounds); + mAnimationController.setFinishTaskBounds(taskId, destinationBounds, windowCrop, + float9); } catch (RemoteException e) { Log.d(TAG, "Failed to set finish task bounds", e); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index 7ba906986fa3..06155bc7100c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -137,8 +137,11 @@ public class RemoteTransitionCompat implements Parcelable { mWrapped.hideCurrentInputMethod(); } - @Override public void setFinishTaskBounds(int taskId, Rect destinationBounds) { - if (mWrapped != null) mWrapped.setFinishTaskBounds(taskId, destinationBounds); + @Override public void setFinishTaskBounds(int taskId, Rect destinationBounds, + Rect windowCrop, float[] float9) { + if (mWrapped != null) { + mWrapped.setFinishTaskBounds(taskId, destinationBounds, windowCrop, float9); + } } @Override public void finish(boolean toHome, boolean sendUserLeaveHint) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 7eac9034f02a..ccba1d59c8d0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -48,7 +48,6 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Gefingerpoken; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -74,8 +73,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; private final SecurityCallback mSecurityCallback; private final ConfigurationController mConfigurationController; - private final KeyguardViewController mKeyguardViewController; - private final FalsingManager mFalsingManager; private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid; @@ -97,13 +94,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } mTouchDown = MotionEvent.obtain(ev); } else if (mTouchDown != null) { - boolean tapResult = mFalsingManager.isFalseTap(true, 0.6); - if (tapResult - || ev.getActionMasked() == MotionEvent.ACTION_UP + if (ev.getActionMasked() == MotionEvent.ACTION_UP || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { - if (tapResult) { - mKeyguardViewController.reset(true); - } mTouchDown.recycle(); mTouchDown = null; } @@ -207,9 +199,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard KeyguardStateController keyguardStateController, SecurityCallback securityCallback, KeyguardSecurityViewFlipperController securityViewFlipperController, - ConfigurationController configurationController, - KeyguardViewController keyguardViewController, - FalsingManager falsingManager) { + ConfigurationController configurationController) { super(view); mLockPatternUtils = lockPatternUtils; mUpdateMonitor = keyguardUpdateMonitor; @@ -222,8 +212,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create( mKeyguardSecurityCallback); mConfigurationController = configurationController; - mKeyguardViewController = keyguardViewController; - mFalsingManager = falsingManager; } @Override @@ -523,8 +511,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final KeyguardStateController mKeyguardStateController; private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; private final ConfigurationController mConfigurationController; - private final KeyguardViewController mKeyguardViewController; - private final FalsingManager mFalsingManager; @Inject Factory(KeyguardSecurityContainer view, @@ -537,9 +523,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, KeyguardSecurityViewFlipperController securityViewFlipperController, - ConfigurationController configurationController, - KeyguardViewController keyguardViewController, - FalsingManager falsingManager) { + ConfigurationController configurationController) { mView = view; mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory; mLockPatternUtils = lockPatternUtils; @@ -550,8 +534,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardStateController = keyguardStateController; mSecurityViewFlipperController = securityViewFlipperController; mConfigurationController = configurationController; - mKeyguardViewController = keyguardViewController; - mFalsingManager = falsingManager; } public KeyguardSecurityContainerController create( @@ -560,7 +542,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, securityCallback, mSecurityViewFlipperController, - mConfigurationController, mKeyguardViewController, mFalsingManager); + mConfigurationController); } } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java index 14b96913ea49..6cee4a4d4ad7 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java @@ -24,7 +24,6 @@ import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.RippleDrawable; import android.view.ContextThemeWrapper; -import android.view.ViewGroup; import androidx.annotation.StyleRes; @@ -41,7 +40,6 @@ class NumPadAnimator { private GradientDrawable mBackground; private RippleDrawable mRipple; private GradientDrawable mRippleMask; - private int mMargin; private int mNormalColor; private int mHighlightColor; private int mStyle; @@ -55,8 +53,6 @@ class NumPadAnimator { reloadColors(context); - mMargin = context.getResources().getDimensionPixelSize(R.dimen.num_pad_key_margin); - // Actual values will be updated later, usually during an onLayout() call mAnimator = new AnimatorSet(); mExpandAnimator = ValueAnimator.ofFloat(0f, 1f); @@ -82,10 +78,6 @@ class NumPadAnimator { mAnimator.playSequentially(mExpandAnimator, mContractAnimator); } - void updateMargin(ViewGroup.MarginLayoutParams lp) { - lp.setMargins(mMargin, mMargin, mMargin, mMargin); - } - void onLayout(int height) { float startRadius = height / 2f; float endRadius = height / 4f; diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java index 1635c49b5668..b76499a39ff8 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java @@ -23,7 +23,6 @@ import android.graphics.drawable.VectorDrawable; import android.util.AttributeSet; import android.view.ContextThemeWrapper; import android.view.MotionEvent; -import android.view.ViewGroup; import androidx.annotation.Nullable; @@ -51,13 +50,6 @@ public class NumPadButton extends AlphaOptimizedImageButton { } @Override - public void setLayoutParams(ViewGroup.LayoutParams params) { - if (mAnimator != null) mAnimator.updateMargin((ViewGroup.MarginLayoutParams) params); - - super.setLayoutParams(params); - } - - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 96fceeeb8397..89c1a7fe21ca 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -178,13 +178,6 @@ public class NumPadKey extends ViewGroup { } @Override - public void setLayoutParams(ViewGroup.LayoutParams params) { - if (mAnimator != null) mAnimator.updateMargin((ViewGroup.MarginLayoutParams) params); - - super.setLayoutParams(params); - } - - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index a9b4c492e10c..7966b388bc4f 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -25,9 +25,12 @@ import android.os.HandlerThread; import android.os.SystemClock; import android.os.Trace; import android.service.wallpaper.WallpaperService; +import android.util.ArraySet; import android.util.Log; +import android.util.MathUtils; import android.util.Size; import android.view.SurfaceHolder; +import android.view.WindowManager; import androidx.annotation.NonNull; @@ -54,7 +57,10 @@ public class ImageWallpaper extends WallpaperService { private static final @android.annotation.NonNull RectF LOCAL_COLOR_BOUNDS = new RectF(0, 0, 1, 1); private static final boolean DEBUG = false; - private ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>(); + private final ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>(); + private final ArraySet<RectF> mColorAreas = new ArraySet<>(); + private float mShift; + private volatile int mPages; private HandlerThread mWorker; // scaled down version private Bitmap mMiniBitmap; @@ -96,6 +102,10 @@ public class ImageWallpaper extends WallpaperService { private EglHelper mEglHelper; private final Runnable mFinishRenderingTask = this::finishRendering; private boolean mNeedRedraw; + private int mWidth = 1; + private int mHeight = 1; + private int mImgWidth = 1; + private int mImgHeight = 1; GLEngine() { } @@ -111,8 +121,13 @@ public class ImageWallpaper extends WallpaperService { // Deferred init renderer because we need to get wallpaper by display context. mRenderer = getRendererInstance(); setFixedSizeAllowed(true); - setOffsetNotificationsEnabled(false); updateSurfaceSize(); + Rect window = getDisplayContext() + .getSystemService(WindowManager.class) + .getCurrentWindowMetrics() + .getBounds(); + mHeight = window.height(); + mWidth = window.width(); mMiniBitmap = null; if (mWorker != null && mWorker.getThreadHandler() != null) { mWorker.getThreadHandler().post(this::updateMiniBitmap); @@ -127,6 +142,41 @@ public class ImageWallpaper extends WallpaperService { return new ImageWallpaperRenderer(getDisplayContext()); } + @Override + public void onOffsetsChanged(float xOffset, float yOffset, + float xOffsetStep, float yOffsetStep, + int xPixelOffset, int yPixelOffset) { + if (mMiniBitmap == null || mMiniBitmap.isRecycled()) return; + final int pages; + if (xOffsetStep > 0 && xOffsetStep <= 1) { + pages = (int) (1 / xOffsetStep + 1); + } else { + pages = 1; + } + if (pages == mPages) return; + mPages = pages; + updateShift(); + mWorker.getThreadHandler().post(() -> + computeAndNotifyLocalColors(new ArrayList<>(mColorAreas), mMiniBitmap)); + } + + private void updateShift() { + if (mImgHeight == 0) { + mShift = 0; + return; + } + // calculate shift + float imgWidth = (float) mImgWidth / (float) mImgHeight; + float displayWidth = + (float) mWidth / (float) mHeight; + // if need to shift + if (imgWidth > displayWidth) { + mShift = imgWidth / imgWidth - displayWidth / imgWidth; + } else { + mShift = 0; + } + } + private void updateMiniBitmap() { mRenderer.useBitmap(b -> { int size = Math.min(b.getWidth(), b.getHeight()); @@ -134,6 +184,8 @@ public class ImageWallpaper extends WallpaperService { if (size > MIN_SURFACE_WIDTH) { scale = (float) MIN_SURFACE_WIDTH / (float) size; } + mImgHeight = b.getHeight(); + mImgWidth = b.getWidth(); mMiniBitmap = Bitmap.createScaledBitmap(b, Math.round(scale * b.getWidth()), Math.round(scale * b.getHeight()), false); computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap); @@ -173,6 +225,9 @@ public class ImageWallpaper extends WallpaperService { @Override public void addLocalColorsAreas(@NonNull List<RectF> regions) { mWorker.getThreadHandler().post(() -> { + if (mColorAreas.size() + mLocalColorsToAdd.size() == 0) { + setOffsetNotificationsEnabled(true); + } Bitmap bitmap = mMiniBitmap; if (bitmap == null) { mLocalColorsToAdd.addAll(regions); @@ -184,6 +239,7 @@ public class ImageWallpaper extends WallpaperService { private void computeAndNotifyLocalColors(@NonNull List<RectF> regions, Bitmap b) { List<WallpaperColors> colors = getLocalWallpaperColors(regions, b); + mColorAreas.addAll(regions); try { notifyLocalColorsChanged(regions, colors); } catch (RuntimeException e) { @@ -193,14 +249,45 @@ public class ImageWallpaper extends WallpaperService { @Override public void removeLocalColorsAreas(@NonNull List<RectF> regions) { - // No-OP + mWorker.getThreadHandler().post(() -> { + mColorAreas.removeAll(regions); + mLocalColorsToAdd.removeAll(regions); + if (mColorAreas.size() + mLocalColorsToAdd.size() == 0) { + setOffsetNotificationsEnabled(false); + } + }); + } + + private RectF pageToImgRect(RectF area) { + float pageWidth = 1f / (float) mPages; + if (pageWidth < 1 && pageWidth >= 0) pageWidth = 1; + float imgWidth = (float) mImgWidth / (float) mImgHeight; + float displayWidth = + (float) mWidth / (float) mHeight; + float expansion = imgWidth > displayWidth ? displayWidth / imgWidth : 1; + int page = (int) Math.floor(area.centerX() / pageWidth); + float shiftWidth = mShift * page * pageWidth; + RectF imgArea = new RectF(); + imgArea.bottom = area.bottom; + imgArea.top = area.top; + imgArea.left = MathUtils.constrain(area.left % pageWidth, 0, 1) + * expansion + shiftWidth; + imgArea.right = MathUtils.constrain(area.right % pageWidth, 0, 1) + * expansion + shiftWidth; + if (imgArea.left > imgArea.right) { + // take full page + imgArea.left = shiftWidth; + imgArea.right = 1 - (mShift - shiftWidth); + } + return imgArea; } private List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas, Bitmap b) { List<WallpaperColors> colors = new ArrayList<>(areas.size()); + updateShift(); for (int i = 0; i < areas.size(); i++) { - RectF area = areas.get(i); + RectF area = pageToImgRect(areas.get(i)); if (area == null || !LOCAL_COLOR_BOUNDS.contains(area)) { colors.add(null); continue; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index 2036150d3679..60fdbab8482c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -71,8 +71,16 @@ abstract class UdfpsAnimationView extends FrameLayout { return false; } - private void updateAlpha() { - getDrawable().setAlpha(mPauseAuth ? mAlpha : 255); + protected void updateAlpha() { + getDrawable().setAlpha(calculateAlpha()); + } + + protected final int calculateAlpha() { + return mPauseAuth ? mAlpha : 255; + } + + boolean isPauseAuth() { + return mPauseAuth; } private int expansionToAlpha(float expansion) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java index b6d80ba14dc0..b7726f41e4a8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java @@ -24,10 +24,16 @@ import android.annotation.NonNull; import android.graphics.PointF; import android.graphics.RectF; +import com.android.systemui.Dumpable; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.ViewController; +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * Handles: * 1. registering for listeners when its view is attached and unregistering on view detached @@ -39,33 +45,50 @@ import com.android.systemui.util.ViewController; * - doze time event */ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView> - extends ViewController<T> { + extends ViewController<T> implements Dumpable { @NonNull final StatusBarStateController mStatusBarStateController; @NonNull final StatusBar mStatusBar; + @NonNull final DumpManager mDumpManger; private boolean mNotificationShadeExpanded; private int mStatusBarState; protected UdfpsAnimationViewController( T view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { super(view); mStatusBarStateController = statusBarStateController; mStatusBar = statusBar; + mDumpManger = dumpManager; } + abstract @NonNull String getTag(); + @Override protected void onViewAttached() { mStatusBarStateController.addCallback(mStateListener); mStateListener.onStateChanged(mStatusBarStateController.getState()); mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener); + + mDumpManger.registerDumpable(getTag(), this); } @Override protected void onViewDetached() { mStatusBarStateController.removeCallback(mStateListener); mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener); + + mDumpManger.unregisterDumpable(getTag()); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("mStatusBarState=" + StatusBarState.toShortString(mStatusBarState)); + pw.println("mNotificationShadeExpanded=" + mNotificationShadeExpanded); + pw.println("shouldPauseAuth()=" + shouldPauseAuth()); + pw.println("isPauseAuth=" + mView.isPauseAuth()); } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java index b712c655a6e7..93d80e29aded 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java @@ -16,6 +16,9 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; + +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -24,9 +27,15 @@ import com.android.systemui.statusbar.phone.StatusBar; */ class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> { protected UdfpsBpViewController( - UdfpsBpView view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull UdfpsBpView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); + } + + @Override + @NonNull String getTag() { + return "UdfpsBpViewController"; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 94aeb73c4b42..797a4411b8c9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -50,8 +50,10 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.util.concurrency.DelayableExecutor; import javax.inject.Inject; @@ -83,6 +85,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private final DelayableExecutor mFgExecutor; @NonNull private final StatusBar mStatusBar; @NonNull private final StatusBarStateController mStatusBarStateController; + @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; + @NonNull private final DumpManager mDumpManager; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; @@ -299,7 +303,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { WindowManager windowManager, @NonNull StatusBarStateController statusBarStateController, @Main DelayableExecutor fgExecutor, - @NonNull StatusBar statusBar) { + @NonNull StatusBar statusBar, + @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, + @NonNull DumpManager dumpManager) { mContext = context; mInflater = inflater; // The fingerprint manager is queried for UDFPS before this class is constructed, so the @@ -309,6 +315,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mFgExecutor = fgExecutor; mStatusBar = statusBar; mStatusBarStateController = statusBarStateController; + mKeyguardViewManager = statusBarKeyguardViewManager; + mDumpManager = dumpManager; mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists @@ -457,7 +465,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { enrollView, mServerRequest.mEnrollHelper, mStatusBarStateController, - mStatusBar + mStatusBar, + mDumpManager ); case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: UdfpsKeyguardView keyguardView = (UdfpsKeyguardView) @@ -466,7 +475,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return new UdfpsKeyguardViewController( keyguardView, mStatusBarStateController, - mStatusBar + mStatusBar, + mKeyguardViewManager, + mDumpManager ); case IUdfpsOverlayController.REASON_AUTH_BP: // note: empty controller, currently shows no visual affordance @@ -475,7 +486,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return new UdfpsBpViewController( bpView, mStatusBarStateController, - mStatusBar + mStatusBar, + mDumpManager ); case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView) @@ -484,7 +496,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return new UdfpsFpmOtherViewController( authOtherView, mStatusBarStateController, - mStatusBar + mStatusBar, + mDumpManager ); default: Log.d(TAG, "Animation for reason " + reason + " not supported yet"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java index 13d31cb87fdc..18f54166ad28 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java @@ -61,12 +61,14 @@ public abstract class UdfpsDrawable extends Drawable { */ protected void updateFingerprintIconBounds(@NonNull Rect bounds) { mFingerprintDrawable.setBounds(bounds); + invalidateSelf(); } @Override public void setAlpha(int alpha) { mAlpha = alpha; mFingerprintDrawable.setAlpha(mAlpha); + invalidateSelf(); } boolean isIlluminationShowing() { @@ -74,7 +76,11 @@ public abstract class UdfpsDrawable extends Drawable { } void setIlluminationShowing(boolean showing) { + if (mIlluminationShowing == showing) { + return; + } mIlluminationShowing = showing; + invalidateSelf(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java index d80e085bdc70..cd5abd74c260 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java @@ -17,7 +17,6 @@ package com.android.systemui.biometrics; import android.content.Context; -import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -37,11 +36,10 @@ import com.android.systemui.R; public class UdfpsEnrollDrawable extends UdfpsDrawable { private static final String TAG = "UdfpsAnimationEnroll"; - private static final float SHADOW_RADIUS = 5.f; - static final float PROGRESS_BAR_RADIUS = 140.f; + static final float PROGRESS_BAR_RADIUS = 180.f; @NonNull private final Drawable mMovingTargetFpIcon; - @NonNull private final Paint mSensorPaint; + @NonNull private final Paint mSensorOutlinePaint; @NonNull private final Paint mBlueFill; @NonNull private final Paint mBlueStroke; @@ -51,11 +49,11 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { UdfpsEnrollDrawable(@NonNull Context context) { super(context); - mSensorPaint = new Paint(0 /* flags */); - mSensorPaint.setAntiAlias(true); - mSensorPaint.setColor(Color.WHITE); - mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, Color.BLACK); - mSensorPaint.setStyle(Paint.Style.FILL); + mSensorOutlinePaint = new Paint(0 /* flags */); + mSensorOutlinePaint.setAntiAlias(true); + mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon)); + mSensorOutlinePaint.setStyle(Paint.Style.STROKE); + mSensorOutlinePaint.setStrokeWidth(2.f); mBlueFill = new Paint(0 /* flags */); mBlueFill.setAntiAlias(true); @@ -98,18 +96,15 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { return; } - final boolean isNightMode = (mContext.getResources().getConfiguration().uiMode - & Configuration.UI_MODE_NIGHT_YES) != 0; - if (!isNightMode) { - if (mSensorRect != null) { - canvas.drawOval(mSensorRect, mSensorPaint); - } + if (mSensorRect != null) { + canvas.drawOval(mSensorRect, mSensorOutlinePaint); } mFingerprintDrawable.draw(canvas); // Draw moving target if (mEnrollHelper.isCenterEnrollmentComplete()) { mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha); + mSensorOutlinePaint.setAlpha(mAlpha == 255 ? 64 : mAlpha); canvas.save(); final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint(); @@ -123,15 +118,17 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { canvas.restore(); } else { mFingerprintDrawable.setAlpha(mAlpha); + mSensorOutlinePaint.setAlpha(mAlpha); } } @Override public void setAlpha(int alpha) { super.setAlpha(alpha); - mSensorPaint.setAlpha(alpha); + mSensorOutlinePaint.setAlpha(alpha); mBlueFill.setAlpha(alpha); mBlueStroke.setAlpha(alpha); + mMovingTargetFpIcon.setAlpha(alpha); invalidateSelf(); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java index 7985d95c7c61..75e8638e43df 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.util.AttributeSet; import android.widget.ImageView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.systemui.R; @@ -28,8 +29,9 @@ import com.android.systemui.R; * View corresponding with udfps_enroll_view.xml */ public class UdfpsEnrollView extends UdfpsAnimationView { - private final UdfpsEnrollDrawable mFingerprintDrawable; - private ImageView mFingerprintView; + @NonNull private final UdfpsEnrollDrawable mFingerprintDrawable; + @NonNull private ImageView mFingerprintView; + @NonNull private UdfpsProgressBar mProgressBar; public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -37,9 +39,17 @@ public class UdfpsEnrollView extends UdfpsAnimationView { } @Override + protected void updateAlpha() { + super.updateAlpha(); + mProgressBar.setAlpha(calculateAlpha()); + mProgressBar.getProgressDrawable().setAlpha(calculateAlpha()); + } + + @Override protected void onFinishInflate() { mFingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view); mFingerprintView.setImageDrawable(mFingerprintDrawable); + mProgressBar = findViewById(R.id.progress_bar); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java index da8d712ebbdc..1ebbfbf84814 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java @@ -21,6 +21,7 @@ import android.graphics.PointF; import android.view.View; import com.android.systemui.R; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -32,17 +33,23 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp @NonNull private final UdfpsEnrollHelper mEnrollHelper; protected UdfpsEnrollViewController( - UdfpsEnrollView view, + @NonNull UdfpsEnrollView view, @NonNull UdfpsEnrollHelper enrollHelper, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); mEnrollHelper = enrollHelper; mProgressBar = mView.findViewById(R.id.progress_bar); mView.setEnrollHelper(mEnrollHelper); } @Override + @NonNull String getTag() { + return "UdfpsEnrollViewController"; + } + + @Override protected void onViewAttached() { super.onViewAttached(); if (mEnrollHelper.shouldShowProgressBar()) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java index 587501bd1aa5..6e2e4baf492b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java @@ -16,6 +16,9 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; + +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -27,9 +30,15 @@ import com.android.systemui.statusbar.phone.StatusBar; */ class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmOtherView> { protected UdfpsFpmOtherViewController( - UdfpsFpmOtherView view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull UdfpsFpmOtherView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); + } + + @Override + @NonNull String getTag() { + return "UdfpsFpmOtherViewController"; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java index b0c5da09d916..12c15a0882f9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java @@ -36,14 +36,14 @@ import com.android.systemui.doze.DozeReceiver; public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver { private static final String TAG = "UdfpsAnimationKeyguard"; - private final int mLockScreenColor; private final int mAmbientDisplayColor; @NonNull private final Context mContext; - private final int mMaxBurnInOffsetX; - private final int mMaxBurnInOffsetY; + private int mLockScreenColor; // AOD anti-burn-in offsets + private final int mMaxBurnInOffsetX; + private final int mMaxBurnInOffsetY; private float mInterpolatedDarkAmount; private float mBurnInOffsetX; private float mBurnInOffsetY; @@ -95,4 +95,10 @@ public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver mInterpolatedDarkAmount = eased; updateAodPositionAndColor(); } + + void setLockScreenColor(int color) { + if (mLockScreenColor == color) return; + mLockScreenColor = color; + updateAodPositionAndColor(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java index 6a9356034d22..a78c223bf883 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -16,13 +16,23 @@ package com.android.systemui.biometrics; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.content.Context; import android.util.AttributeSet; +import android.view.View; import android.widget.ImageView; import androidx.annotation.Nullable; +import com.android.settingslib.Utils; +import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.StatusBarState; /** * View corresponding with udfps_keyguard_view.xml @@ -30,6 +40,14 @@ import com.android.systemui.R; public class UdfpsKeyguardView extends UdfpsAnimationView { private final UdfpsKeyguardDrawable mFingerprintDrawable; private ImageView mFingerprintView; + private int mWallpaperTexColor; + private int mStatusBarState; + + // used when highlighting fp icon: + private int mTextColorPrimary; + private ImageView mBgProtection; + + private AnimatorSet mAnimatorSet; public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -38,8 +56,15 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { @Override protected void onFinishInflate() { + super.onFinishInflate(); mFingerprintView = findViewById(R.id.udfps_keyguard_animation_fp_view); - mFingerprintView.setImageDrawable(mFingerprintDrawable); + mFingerprintView.setForeground(mFingerprintDrawable); + + mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg); + + mWallpaperTexColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); + mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorPrimary); } @Override @@ -54,7 +79,114 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { return true; } + void setStatusBarState(int statusBarState) { + mStatusBarState = statusBarState; + if (!isShadeLocked()) { + mFingerprintView.setAlpha(1f); + mFingerprintDrawable.setLockScreenColor(mWallpaperTexColor); + } + } + void onDozeAmountChanged(float linear, float eased) { mFingerprintDrawable.onDozeAmountChanged(linear, eased); + invalidate(); + } + + /** + * Animates in the bg protection circle behind the fp icon to highlight the icon. + */ + void animateHighlightFp() { + if (mBgProtection.getVisibility() == View.VISIBLE && mBgProtection.getAlpha() == 1f) { + // already fully highlighted, don't re-animate + return; + } + + if (mAnimatorSet != null) { + mAnimatorSet.cancel(); + } + ValueAnimator fpIconAnim; + if (isShadeLocked()) { + // set color and fade in since we weren't showing before + mFingerprintDrawable.setLockScreenColor(mTextColorPrimary); + fpIconAnim = ObjectAnimator.ofFloat(mFingerprintView, View.ALPHA, 0f, 1f); + } else { + // update icon color + fpIconAnim = new ValueAnimator(); + fpIconAnim.setIntValues(mWallpaperTexColor, mTextColorPrimary); + fpIconAnim.setEvaluator(new ArgbEvaluator()); + fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor( + (Integer) valueAnimator.getAnimatedValue())); + } + + mAnimatorSet = new AnimatorSet(); + mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mAnimatorSet.setDuration(500); + mAnimatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mBgProtection.setVisibility(View.VISIBLE); + } + }); + + mAnimatorSet.playTogether( + ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f), + fpIconAnim); + mAnimatorSet.start(); + } + + private boolean isShadeLocked() { + return mStatusBarState == StatusBarState.SHADE_LOCKED; + } + + /** + * Animates out the bg protection circle behind the fp icon to unhighlight the icon. + */ + void animateUnhighlightFp(@Nullable Runnable onEndAnimation) { + if (mBgProtection.getVisibility() == View.GONE) { + // already hidden + return; + } + + if (mAnimatorSet != null) { + mAnimatorSet.cancel(); + } + ValueAnimator fpIconAnim; + if (isShadeLocked()) { + // fade out + fpIconAnim = ObjectAnimator.ofFloat(mFingerprintView, View.ALPHA, 1f, 0f); + } else { + // update icon color + fpIconAnim = new ValueAnimator(); + fpIconAnim.setIntValues(mTextColorPrimary, mWallpaperTexColor); + fpIconAnim.setEvaluator(new ArgbEvaluator()); + fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor( + (Integer) valueAnimator.getAnimatedValue())); + } + + mAnimatorSet = new AnimatorSet(); + mAnimatorSet.playTogether( + ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 1f, 0f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 1f, 0f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 1f, 0f), + fpIconAnim); + mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mAnimatorSet.setDuration(500); + + mAnimatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBgProtection.setVisibility(View.GONE); + if (onEndAnimation != null) { + onEndAnimation.run(); + } + } + }); + mAnimatorSet.start(); + } + + boolean isAnimating() { + return mAnimatorSet != null && mAnimatorSet.isRunning(); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 14bb3fee1174..08e5d562113d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -16,34 +16,62 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; + +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; + +import java.io.FileDescriptor; +import java.io.PrintWriter; /** * Class that coordinates non-HBM animations during keyguard authentication. */ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> { + @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; + private boolean mForceShow; protected UdfpsKeyguardViewController( - UdfpsKeyguardView view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull UdfpsKeyguardView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); + mKeyguardViewManager = statusBarKeyguardViewManager; + } + + @Override + @NonNull String getTag() { + return "UdfpsKeyguardViewController"; } @Override protected void onViewAttached() { super.onViewAttached(); - mStatusBarStateController.addCallback(mStateListener); + final float dozeAmount = mStatusBarStateController.getDozeAmount(); + mStatusBarStateController.addCallback(mStateListener); mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount); + mStateListener.onStateChanged(mStatusBarStateController.getState()); + mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); } @Override protected void onViewDetached() { super.onViewDetached(); mStatusBarStateController.removeCallback(mStateListener); + mAlternateAuthInterceptor.reset(); + mKeyguardViewManager.setAlternateAuthInterceptor(null); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + super.dump(fd, pw, args); + pw.println("mForceShow=" + mForceShow); } /** @@ -56,7 +84,11 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud mForceShow = forceShow; updatePauseAuth(); - // TODO: animate show/hide background protection + if (mForceShow) { + mView.animateHighlightFp(); + } else { + mView.animateUnhighlightFp(() -> mKeyguardViewManager.cancelPostAuthActions()); + } } /** @@ -76,6 +108,50 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @Override public void onDozeAmountChanged(float linear, float eased) { mView.onDozeAmountChanged(linear, eased); + if (linear != 0) forceShow(false); + } + + @Override + public void onStateChanged(int statusBarState) { + mView.setStatusBarState(statusBarState); } }; + + private final StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor = + new StatusBarKeyguardViewManager.AlternateAuthInterceptor() { + @Override + public boolean showAlternativeAuthMethod() { + if (mForceShow) { + return false; + } + + forceShow(true); + return true; + } + + @Override + public boolean reset() { + if (!mForceShow) { + return false; + } + + forceShow(false); + return true; + } + + @Override + public boolean isShowingAlternativeAuth() { + return mForceShow; + } + + @Override + public boolean isAnimating() { + return mView.isAnimating(); + } + + @Override + public void dump(PrintWriter pw) { + pw.print(getTag()); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java index efb799294004..292af3f069f5 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java @@ -28,9 +28,11 @@ import androidx.annotation.NonNull; import com.android.internal.logging.MetricsLogger; import com.android.systemui.classifier.FalsingDataProvider.SessionListener; +import com.android.systemui.classifier.HistoryTracker.BeliefListener; import com.android.systemui.dagger.qualifiers.TestHarness; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.sensors.ThresholdSensor; import java.io.FileDescriptor; @@ -61,12 +63,14 @@ public class BrightLineFalsingManager implements FalsingManager { private static final int RECENT_INFO_LOG_SIZE = 40; private static final int RECENT_SWIPE_LOG_SIZE = 20; private static final double TAP_CONFIDENCE_THRESHOLD = 0.7; + private static final double FALSE_BELIEF_THRESHOLD = 0.9; private final FalsingDataProvider mDataProvider; private final DockManager mDockManager; private final SingleTapClassifier mSingleTapClassifier; private final DoubleTapClassifier mDoubleTapClassifier; private final HistoryTracker mHistoryTracker; + private final KeyguardStateController mKeyguardStateController; private final boolean mTestHarness; private final MetricsLogger mMetricsLogger; private int mIsFalseTouchCalls; @@ -76,6 +80,7 @@ public class BrightLineFalsingManager implements FalsingManager { new ArrayDeque<>(RECENT_SWIPE_LOG_SIZE + 1); private final Collection<FalsingClassifier> mClassifiers; + private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); private final SessionListener mSessionListener = new SessionListener() { @Override @@ -89,22 +94,28 @@ public class BrightLineFalsingManager implements FalsingManager { } }; + private final BeliefListener mBeliefListener = belief -> { + if (belief > FALSE_BELIEF_THRESHOLD) { + mFalsingBeliefListeners.forEach(FalsingBeliefListener::onFalse); + } + }; + private final FalsingDataProvider.GestureCompleteListener mGestureCompleteListener = new FalsingDataProvider.GestureCompleteListener() { @Override - public void onGestureComplete(long completionTimeMs) { - if (mPriorResults != null) { - mHistoryTracker.addResults(mPriorResults, completionTimeMs); - mPriorResults = null; - } else { - // Gestures that were not classified get treated as a false. - mHistoryTracker.addResults( - Collections.singleton( - FalsingClassifier.Result.falsed(.8, "unclassified")), - completionTimeMs); - } - } - }; + public void onGestureComplete(long completionTimeMs) { + if (mPriorResults != null) { + mHistoryTracker.addResults(mPriorResults, completionTimeMs); + mPriorResults = null; + } else { + // Gestures that were not classified get treated as a false. + mHistoryTracker.addResults( + Collections.singleton( + FalsingClassifier.Result.falsed(.8, "unclassified")), + completionTimeMs); + } + } + }; private Collection<FalsingClassifier.Result> mPriorResults; @@ -113,7 +124,8 @@ public class BrightLineFalsingManager implements FalsingManager { DockManager dockManager, MetricsLogger metricsLogger, @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers, SingleTapClassifier singleTapClassifier, DoubleTapClassifier doubleTapClassifier, - HistoryTracker historyTracker, @TestHarness boolean testHarness) { + HistoryTracker historyTracker, KeyguardStateController keyguardStateController, + @TestHarness boolean testHarness) { mDataProvider = falsingDataProvider; mDockManager = dockManager; mMetricsLogger = metricsLogger; @@ -121,10 +133,12 @@ public class BrightLineFalsingManager implements FalsingManager { mSingleTapClassifier = singleTapClassifier; mDoubleTapClassifier = doubleTapClassifier; mHistoryTracker = historyTracker; + mKeyguardStateController = keyguardStateController; mTestHarness = testHarness; mDataProvider.addSessionListener(mSessionListener); mDataProvider.addGestureCompleteListener(mGestureCompleteListener); + mHistoryTracker.addBeliefListener(mBeliefListener); } @Override @@ -134,6 +148,10 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public boolean isFalseTouch(@Classifier.InteractionType int interactionType) { + if (skipFalsing()) { + return false; + } + boolean result; mDataProvider.setInteractionType(interactionType); @@ -195,6 +213,10 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public boolean isFalseTap(boolean robustCheck, double falsePenalty) { + if (skipFalsing()) { + return false; + } + FalsingClassifier.Result singleTapResult = mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents()); mPriorResults = Collections.singleton(singleTapResult); @@ -233,6 +255,10 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public boolean isFalseDoubleTap() { + if (skipFalsing()) { + return false; + } + FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture(); mPriorResults = Collections.singleton(result); if (result.isFalse()) { @@ -246,6 +272,10 @@ public class BrightLineFalsingManager implements FalsingManager { return result.isFalse(); } + private boolean skipFalsing() { + return !mKeyguardStateController.isShowing(); + } + @Override public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { // TODO: some of these classifiers might allow us to abort early, meaning we don't have to @@ -282,6 +312,16 @@ public class BrightLineFalsingManager implements FalsingManager { } @Override + public void addFalsingBeliefListener(FalsingBeliefListener listener) { + mFalsingBeliefListeners.add(listener); + } + + @Override + public void removeFalsingBeliefListener(FalsingBeliefListener listener) { + mFalsingBeliefListeners.remove(listener); + } + + @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.println("BRIGHTLINE FALSING MANAGER"); @@ -321,6 +361,8 @@ public class BrightLineFalsingManager implements FalsingManager { mDataProvider.removeSessionListener(mSessionListener); mDataProvider.removeGestureCompleteListener(mGestureCompleteListener); mClassifiers.forEach(FalsingClassifier::cleanup); + mFalsingBeliefListeners.clear(); + mHistoryTracker.removeBeliefListener(mBeliefListener); } static void logDebug(String msg) { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java index e090006cca4f..b359860a0fd7 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java @@ -27,6 +27,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ThresholdSensor; import com.android.systemui.util.time.SystemClock; @@ -48,6 +49,7 @@ class FalsingCollectorImpl implements FalsingCollector { private final HistoryTracker mHistoryTracker; private final ProximitySensor mProximitySensor; private final StatusBarStateController mStatusBarStateController; + private final KeyguardStateController mKeyguardStateController; private final SystemClock mSystemClock; private int mState; @@ -87,13 +89,14 @@ class FalsingCollectorImpl implements FalsingCollector { FalsingCollectorImpl(FalsingDataProvider falsingDataProvider, FalsingManager falsingManager, KeyguardUpdateMonitor keyguardUpdateMonitor, HistoryTracker historyTracker, ProximitySensor proximitySensor, StatusBarStateController statusBarStateController, - SystemClock systemClock) { + KeyguardStateController keyguardStateController, SystemClock systemClock) { mFalsingDataProvider = falsingDataProvider; mFalsingManager = falsingManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mHistoryTracker = historyTracker; mProximitySensor = proximitySensor; mStatusBarStateController = statusBarStateController; + mKeyguardStateController = keyguardStateController; mSystemClock = systemClock; @@ -255,6 +258,10 @@ class FalsingCollectorImpl implements FalsingCollector { @Override public void onTouchEvent(MotionEvent ev) { + if (!mKeyguardStateController.isShowing()) { + avoidGesture(); + return; + } // We delay processing down events to see if another component wants to process them. // If #avoidGesture is called after a MotionEvent.ACTION_DOWN, all following motion events // will be ignored by the collector until another MotionEvent.ACTION_DOWN is passed in. @@ -276,8 +283,8 @@ class FalsingCollectorImpl implements FalsingCollector { @Override public void avoidGesture() { + mAvoidGesture = true; if (mPendingDownEvent != null) { - mAvoidGesture = true; mPendingDownEvent.recycle(); mPendingDownEvent = null; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index aac27cb43376..d39f12488595 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -24,6 +24,8 @@ import com.android.systemui.util.sensors.ThresholdSensor; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; /** * Simple Fake for testing where {@link FalsingManager} is required. @@ -38,6 +40,8 @@ public class FalsingManagerFake implements FalsingManager { private boolean mIsReportingEnabled; private boolean mIsFalseRobustTap; + private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); + @Override public void onSuccessfulUnlock() { @@ -127,4 +131,14 @@ public class FalsingManagerFake implements FalsingManager { public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { } + + @Override + public void addFalsingBeliefListener(FalsingBeliefListener listener) { + mFalsingBeliefListeners.add(listener); + } + + @Override + public void removeFalsingBeliefListener(FalsingBeliefListener listener) { + mFalsingBeliefListeners.remove(listener); + } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index e9bb48c7b1a9..9c29f27a2e15 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -161,6 +161,16 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } @Override + public void addFalsingBeliefListener(FalsingBeliefListener listener) { + mInternalFalsingManager.addFalsingBeliefListener(listener); + } + + @Override + public void removeFalsingBeliefListener(FalsingBeliefListener listener) { + mInternalFalsingManager.removeFalsingBeliefListener(listener); + } + + @Override public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { mInternalFalsingManager.onProximityEvent(proximityEvent); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java index be48ec415652..09bf04cb6d59 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java @@ -20,7 +20,9 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.util.time.SystemClock; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; @@ -50,6 +52,7 @@ public class HistoryTracker { private final SystemClock mSystemClock; DelayQueue<CombinedResult> mResults = new DelayQueue<>(); + private final List<BeliefListener> mBeliefListeners = new ArrayList<>(); @Inject HistoryTracker(SystemClock systemClock) { @@ -153,8 +156,17 @@ public class HistoryTracker { } mResults.add(new CombinedResult(uptimeMillis, finalScore)); + + mBeliefListeners.forEach(beliefListener -> beliefListener.onBeliefChanged(falseBelief())); + } + + void addBeliefListener(BeliefListener listener) { + mBeliefListeners.add(listener); } + void removeBeliefListener(BeliefListener listener) { + mBeliefListeners.remove(listener); + } /** * Represents a falsing score combing all the classifiers together. * @@ -197,4 +209,8 @@ public class HistoryTracker { return Long.compare(ourDelay, otherDelay); } } + + interface BeliefListener { + void onBeliefChanged(double belief); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index d02ff91f435b..123ccee80179 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -324,6 +324,7 @@ public class DependencyProvider { /** */ @Provides + @SysUISingleton public AlwaysOnDisplayPolicy provideAlwaysOnDisplayPolicy(Context context) { return new AlwaysOnDisplayPolicy(context); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index ed3d5ec33b41..8f79de518419 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -34,7 +34,7 @@ import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; -import com.android.wm.shell.transition.ShellTransitions; +import com.android.wm.shell.transition.RemoteTransitions; import java.util.Optional; @@ -87,7 +87,7 @@ public interface SysUIComponent { Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump); @BindsInstance - Builder setTransitions(ShellTransitions t); + Builder setTransitions(RemoteTransitions t); @BindsInstance Builder setStartingSurface(Optional<StartingSurface> s); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index bbd95b4d0c90..1b77d1c16639 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -33,7 +33,7 @@ import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; -import com.android.wm.shell.transition.ShellTransitions; +import com.android.wm.shell.transition.RemoteTransitions; import java.util.Optional; @@ -98,7 +98,7 @@ public interface WMComponent { Optional<TaskViewFactory> getTaskViewFactory(); @WMSingleton - ShellTransitions getTransitions(); + RemoteTransitions getTransitions(); @WMSingleton Optional<StartingSurface> getStartingSurface(); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 55c55b97db51..41c9daedf2d4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -476,7 +476,6 @@ class MediaDataManager( Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList() ?: mutableListOf<Int>() // TODO: b/153736623 look into creating actions when this isn't a media style notification - val packageContext: Context = sbn.getPackageContext(context) if (actions != null) { for ((index, action) in actions.withIndex()) { if (action.getIcon() == null) { @@ -499,7 +498,7 @@ class MediaDataManager( null } val mediaActionIcon = if (action.getIcon()?.getType() == Icon.TYPE_RESOURCE) { - Icon.createWithResource(packageContext, action.getIcon()!!.getResId()) + Icon.createWithResource(sbn.packageName, action.getIcon()!!.getResId()) } else { action.getIcon() }.setTint(themeText) diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index b4716598cc0c..9d298221fb79 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -35,6 +35,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE; import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; @@ -85,6 +86,7 @@ import android.text.TextUtils; import android.util.Log; import android.view.Display; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.IWindowManager; import android.view.InsetsState.InternalInsetsType; import android.view.KeyEvent; @@ -213,6 +215,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private boolean mAllowForceNavBarHandleOpaque; private boolean mForceNavBarHandleOpaque; + private Optional<Long> mHomeButtonLongPressDurationMs; private boolean mIsCurrentUserSetup; /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */ @@ -384,6 +387,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true); + private final Runnable mOnVariableDurationHomeLongClick = () -> { + if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) { + mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback( + HapticFeedbackConstants.LONG_PRESS, + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } + }; + private final ContentObserver mAssistContentObserver = new ContentObserver( new Handler(Looper.getMainLooper())) { @Override @@ -405,6 +416,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mForceNavBarHandleOpaque = properties.getBoolean( NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true); } + + if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) { + mHomeButtonLongPressDurationMs = Optional.of( + properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0) + ).filter(duration -> duration != 0); + reconfigureHomeLongClick(); + } } }; @@ -523,6 +541,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, DeviceConfig.NAMESPACE_SYSTEMUI, NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true); + mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong( + DeviceConfig.NAMESPACE_SYSTEMUI, + HOME_BUTTON_LONG_PRESS_DURATION_MS, + /* defaultValue = */ 0 + )).filter(duration -> duration != 0); DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener); @@ -782,6 +805,22 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } } + private void reconfigureHomeLongClick() { + if (mNavigationBarView == null + || mNavigationBarView.getHomeButton().getCurrentView() == null) { + return; + } + if (mHomeButtonLongPressDurationMs.isPresent()) { + mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false); + mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false); + mNavigationBarView.getHomeButton().setOnLongClickListener(null); + } else { + mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(true); + mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true); + mNavigationBarView.getHomeButton().setOnLongClickListener(this::onHomeLongClick); + } + } + private int deltaRotation(int oldRotation, int newRotation) { int delta = newRotation - oldRotation; if (delta < 0) delta += 4; @@ -792,6 +831,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, pw.println("NavigationBar (displayId=" + mDisplayId + "):"); pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation); pw.println(" mCurrentRotation=" + mCurrentRotation); + pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs); if (mNavigationBarView != null) { pw.println(" mNavigationBarWindowState=" @@ -1121,7 +1161,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); homeButton.setOnTouchListener(this::onHomeTouch); - homeButton.setOnLongClickListener(this::onHomeLongClick); + + reconfigureHomeLongClick(); ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); accessibilityButton.setOnClickListener(this::onAccessibilityClick); @@ -1131,7 +1172,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, updateScreenPinningGestures(); } - private boolean onHomeTouch(View v, MotionEvent event) { + @VisibleForTesting + boolean onHomeTouch(View v, MotionEvent event) { if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) { return true; } @@ -1151,9 +1193,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return true; } } + mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> { + mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration); + }); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: + mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); mStatusBarLazy.get().awakenDreams(); break; } @@ -1516,7 +1562,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } public NavigationBarTransitions getBarTransitions() { - return mNavigationBarView.getBarTransitions(); + if (mNavigationBarView != null) { + return mNavigationBarView.getBarTransitions(); + } + return null; } public void finishBarAnimations() { diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java index e7458a3df801..6295692c788c 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java @@ -110,16 +110,16 @@ public class PeopleProvider extends ContentProvider { } if (DEBUG) Log.i(TAG, "Returning tile preview for shortcutId: " + shortcutId); - RemoteViews view = PeopleSpaceUtils.createRemoteViews(getContext(), tile, 0); final Bundle bundle = new Bundle(); + RemoteViews view = PeopleSpaceUtils.createRemoteViews(getContext(), tile, 0, bundle); bundle.putParcelable(PeopleProviderUtils.RESPONSE_KEY_REMOTE_VIEWS, view); return bundle; } private boolean doesCallerHavePermission() { return getContext().checkPermission( - PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_PERMISSION, - Binder.getCallingPid(), Binder.getCallingUid()) + PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_PERMISSION, + Binder.getCallingPid(), Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED; } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index aa45178b6439..e07e9cff4e7d 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -26,6 +26,10 @@ import static android.app.people.ConversationStatus.ACTIVITY_MEDIA; import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY; import static android.app.people.ConversationStatus.ACTIVITY_UPCOMING_BIRTHDAY; import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE; +import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT; +import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH; +import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT; +import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH; import android.annotation.Nullable; import android.app.INotificationManager; @@ -41,6 +45,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; +import android.content.res.Configuration; import android.database.Cursor; import android.database.SQLException; import android.graphics.Bitmap; @@ -52,13 +57,16 @@ import android.icu.text.MeasureFormat; import android.icu.util.Measure; import android.icu.util.MeasureUnit; import android.net.Uri; +import android.os.Bundle; import android.os.Parcelable; import android.provider.ContactsContract; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; import android.service.notification.StatusBarNotification; import android.text.TextUtils; +import android.util.IconDrawableFactory; import android.util.Log; +import android.util.TypedValue; import android.view.View; import android.widget.RemoteViews; @@ -110,11 +118,17 @@ public class PeopleSpaceUtils { public static final String SHORTCUT_ID = "shortcut_id"; public static final String EMPTY_STRING = ""; - public static final int INVALID_WIDGET_ID = -1; public static final int INVALID_USER_ID = -1; public static final PeopleTileKey EMPTY_KEY = new PeopleTileKey(EMPTY_STRING, INVALID_USER_ID, EMPTY_STRING); + public static final int SMALL_LAYOUT = 0; + public static final int MEDIUM_LAYOUT = 1; + @VisibleForTesting + static final int REQUIRED_WIDTH_FOR_MEDIUM = 146; + private static final int AVATAR_SIZE_FOR_MEDIUM = 56; + private static final int DEFAULT_WIDTH = 146; + private static final int DEFAULT_HEIGHT = 92; private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+"); private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+"); @@ -208,7 +222,8 @@ public class PeopleSpaceUtils { //TODO: Delete app widget id when crash is fixed (b/172932636) continue; } - updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, tile); + Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); + updateAppWidgetViews(appWidgetManager, context, appWidgetId, tile, options); widgetIdToTile.put(appWidgetId, tile); } getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds); @@ -343,7 +358,10 @@ public class PeopleSpaceUtils { .stream() .filter(entry -> entry.getRanking() != null && entry.getRanking().getConversationShortcutInfo() != null) - .collect(Collectors.toMap(PeopleTileKey::new, e -> e)); + .collect(Collectors.toMap(PeopleTileKey::new, e -> e, + // Handle duplicate keys to avoid crashes. + (e1, e2) -> e1.getSbn().getNotification().when + > e2.getSbn().getNotification().when ? e1 : e2)); if (DEBUG) { Log.d(TAG, "Number of visible notifications:" + visibleNotifications.size()); } @@ -401,9 +419,11 @@ public class PeopleSpaceUtils { /** Creates a {@link RemoteViews} for {@code tile}. */ public static RemoteViews createRemoteViews(Context context, - PeopleSpaceTile tile, int appWidgetId) { - RemoteViews viewsForTile = getViewForTile(context, tile); - RemoteViews views = setCommonRemoteViewsFields(context, viewsForTile, tile); + PeopleSpaceTile tile, int appWidgetId, Bundle options) { + int layoutSize = getLayoutSize(context, options); + RemoteViews viewsForTile = getViewForTile(context, tile, layoutSize); + int maxAvatarSize = getMaxAvatarSize(context, options, layoutSize); + RemoteViews views = setCommonRemoteViewsFields(context, viewsForTile, tile, maxAvatarSize); return setLaunchIntents(context, views, tile, appWidgetId); } @@ -411,15 +431,16 @@ public class PeopleSpaceUtils { * The prioritization for the {@code tile} content is missed calls, followed by notification * content, then birthdays, then the most recent status, and finally last interaction. */ - private static RemoteViews getViewForTile(Context context, PeopleSpaceTile tile) { + private static RemoteViews getViewForTile(Context context, PeopleSpaceTile tile, + int layoutSize) { if (Objects.equals(tile.getNotificationCategory(), CATEGORY_MISSED_CALL)) { if (DEBUG) Log.d(TAG, "Create missed call view"); - return createMissedCallRemoteViews(context, tile); + return createMissedCallRemoteViews(context, tile, layoutSize); } if (tile.getNotificationKey() != null) { if (DEBUG) Log.d(TAG, "Create notification view"); - return createNotificationRemoteViews(context, tile); + return createNotificationRemoteViews(context, tile, layoutSize); } // TODO: Add sorting when we expose timestamp of statuses. @@ -429,7 +450,7 @@ public class PeopleSpaceUtils { ConversationStatus birthdayStatus = getBirthdayStatus(tile, statusesForEntireView); if (birthdayStatus != null) { if (DEBUG) Log.d(TAG, "Create birthday view"); - return createStatusRemoteViews(context, birthdayStatus); + return createStatusRemoteViews(context, birthdayStatus, layoutSize); } if (!statusesForEntireView.isEmpty()) { @@ -437,10 +458,48 @@ public class PeopleSpaceUtils { Log.d(TAG, "Create status view for: " + statusesForEntireView.get(0).getActivity()); } - return createStatusRemoteViews(context, statusesForEntireView.get(0)); + return createStatusRemoteViews(context, statusesForEntireView.get(0), layoutSize); + } + + return createLastInteractionRemoteViews(context, tile, layoutSize); + } + + /** Calculates the best layout relative to the size in {@code options}. */ + private static int getLayoutSize(Context context, Bundle options) { + int display = context.getResources().getConfiguration().orientation; + int width = display == Configuration.ORIENTATION_PORTRAIT + ? options.getInt(OPTION_APPWIDGET_MIN_WIDTH, DEFAULT_WIDTH) : options.getInt( + OPTION_APPWIDGET_MAX_WIDTH, DEFAULT_WIDTH); + int height = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt( + OPTION_APPWIDGET_MAX_HEIGHT, DEFAULT_HEIGHT) + : options.getInt(OPTION_APPWIDGET_MIN_HEIGHT, DEFAULT_HEIGHT); + // Small layout used below a certain minimum width with any height. + if (width < REQUIRED_WIDTH_FOR_MEDIUM) { + if (DEBUG) Log.d(TAG, "Small view for width: " + width + " height: " + height); + return SMALL_LAYOUT; } + if (DEBUG) Log.d(TAG, "Medium view for width: " + width + " height: " + height); + return MEDIUM_LAYOUT; + } - return createLastInteractionRemoteViews(context, tile); + /** Returns the max avatar size for {@code layoutSize} under the current {@code options}. */ + private static int getMaxAvatarSize(Context context, Bundle options, int layoutSize) { + int avatarHeightSpace = AVATAR_SIZE_FOR_MEDIUM; + int avatarWidthSpace = AVATAR_SIZE_FOR_MEDIUM; + + if (layoutSize == SMALL_LAYOUT) { + int display = context.getResources().getConfiguration().orientation; + int width = display == Configuration.ORIENTATION_PORTRAIT + ? options.getInt(OPTION_APPWIDGET_MIN_WIDTH, DEFAULT_WIDTH) : options.getInt( + OPTION_APPWIDGET_MAX_WIDTH, DEFAULT_WIDTH); + int height = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt( + OPTION_APPWIDGET_MAX_HEIGHT, DEFAULT_HEIGHT) + : options.getInt(OPTION_APPWIDGET_MIN_HEIGHT, DEFAULT_HEIGHT); + avatarHeightSpace = height - (8 + 4 + 18 + 8); + avatarWidthSpace = width - (4 + 4); + } + if (DEBUG) Log.d(TAG, "Height: " + avatarHeightSpace + " width: " + avatarWidthSpace); + return Math.min(avatarHeightSpace, avatarWidthSpace); } @Nullable @@ -478,14 +537,22 @@ public class PeopleSpaceUtils { } } - private static RemoteViews createStatusRemoteViews(Context context, ConversationStatus status) { - RemoteViews views = new RemoteViews( - context.getPackageName(), R.layout.people_space_small_avatar_tile); + private static RemoteViews createStatusRemoteViews(Context context, ConversationStatus status, + int layoutSize) { + int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view + : R.layout.people_space_small_avatar_tile; + RemoteViews views = new RemoteViews(context.getPackageName(), layout); CharSequence statusText = status.getDescription(); if (TextUtils.isEmpty(statusText)) { statusText = getStatusTextByType(context, status.getActivity()); } - views.setTextViewText(R.id.status, statusText); + views.setViewVisibility(R.id.subtext, View.GONE); + views.setViewVisibility(R.id.text_content, View.VISIBLE); + TypedValue typedValue = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true); + int secondaryTextColor = context.getColor(typedValue.resourceId); + views.setInt(R.id.text_content, "setTextColor", secondaryTextColor); + views.setTextViewText(R.id.text_content, statusText); Icon statusIcon = status.getIcon(); if (statusIcon != null) { views.setImageViewIcon(R.id.image, statusIcon); @@ -494,6 +561,8 @@ public class PeopleSpaceUtils { views.setViewVisibility(R.id.content_background, View.GONE); } // TODO: Set status pre-defined icons + views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person); + ensurePredefinedIconVisibleOnSmallView(views, layoutSize); return views; } @@ -519,7 +588,7 @@ public class PeopleSpaceUtils { } private static RemoteViews setCommonRemoteViewsFields(Context context, RemoteViews views, - PeopleSpaceTile tile) { + PeopleSpaceTile tile, int maxAvatarSize) { try { boolean isAvailable = tile.getStatuses() != null && tile.getStatuses().stream().anyMatch( @@ -532,27 +601,20 @@ public class PeopleSpaceUtils { boolean hasNewStory = tile.getStatuses() != null && tile.getStatuses().stream().anyMatch( c -> c.getActivity() == ACTIVITY_NEW_STORY); - if (hasNewStory) { - views.setViewVisibility(R.id.person_icon_with_story, View.VISIBLE); - views.setViewVisibility(R.id.person_icon_only, View.GONE); - views.setImageViewIcon(R.id.person_icon_inside_ring, tile.getUserIcon()); - } else { - views.setViewVisibility(R.id.person_icon_with_story, View.GONE); - views.setViewVisibility(R.id.person_icon_only, View.VISIBLE); - views.setImageViewIcon(R.id.person_icon_only, tile.getUserIcon()); - } - views.setTextViewText(R.id.name, tile.getUserName().toString()); - views.setImageViewIcon(R.id.person_icon, tile.getUserIcon()); views.setBoolean(R.id.content_background, "setClipToOutline", true); + Icon icon = tile.getUserIcon(); + PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(context, + context.getPackageManager(), + IconDrawableFactory.newInstance(context, false), + maxAvatarSize); + Drawable drawable = icon.loadDrawable(context); + Drawable personDrawable = storyIcon.getPeopleTileDrawable(drawable, + tile.getPackageName(), getUserId(tile), tile.isImportantConversation(), + hasNewStory); + Bitmap bitmap = convertDrawableToBitmap(personDrawable); + views.setImageViewBitmap(R.id.person_icon, bitmap); - views.setImageViewBitmap( - R.id.package_icon, - PeopleSpaceUtils.convertDrawableToBitmap( - context.getPackageManager().getApplicationIcon( - tile.getPackageName()) - ) - ); return views; } catch (Exception e) { Log.e(TAG, "Failed to set common fields: " + e); @@ -585,47 +647,77 @@ public class PeopleSpaceUtils { } catch (Exception e) { Log.e(TAG, "Failed to add launch intents: " + e); } + return views; } private static RemoteViews createMissedCallRemoteViews(Context context, - PeopleSpaceTile tile) { - RemoteViews views = new RemoteViews( - context.getPackageName(), R.layout.people_space_small_avatar_tile); - views.setTextViewText(R.id.status, tile.getNotificationContent()); - views.setImageViewResource(R.id.status_defined_icon, R.drawable.ic_phone_missed); + PeopleSpaceTile tile, int layoutSize) { + int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view + : R.layout.people_space_small_avatar_tile; + RemoteViews views = new RemoteViews(context.getPackageName(), layout); + views.setViewVisibility(R.id.subtext, View.GONE); + views.setViewVisibility(R.id.text_content, View.VISIBLE); + views.setTextViewText(R.id.text_content, tile.getNotificationContent()); + views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_phone_missed); + ensurePredefinedIconVisibleOnSmallView(views, layoutSize); views.setBoolean(R.id.content_background, "setClipToOutline", true); return views; } + private static void ensurePredefinedIconVisibleOnSmallView(RemoteViews views, int layoutSize) { + if (layoutSize == SMALL_LAYOUT) { + views.setViewVisibility(R.id.name, View.GONE); + views.setViewVisibility(R.id.predefined_icon, View.VISIBLE); + } + } + private static RemoteViews createNotificationRemoteViews(Context context, - PeopleSpaceTile tile) { - RemoteViews views = new RemoteViews( - context.getPackageName(), R.layout.people_space_notification_content_tile); + PeopleSpaceTile tile, int layoutSize) { + int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view + : R.layout.people_space_small_avatar_tile; + RemoteViews views = new RemoteViews(context.getPackageName(), layout); + if (layoutSize != MEDIUM_LAYOUT) { + views.setViewVisibility(R.id.name, View.GONE); + views.setViewVisibility(R.id.predefined_icon, View.VISIBLE); + } Uri image = tile.getNotificationDataUri(); + ensurePredefinedIconVisibleOnSmallView(views, layoutSize); if (image != null) { // TODO: Use NotificationInlineImageCache views.setImageViewUri(R.id.image, image); views.setViewVisibility(R.id.content_background, View.VISIBLE); views.setBoolean(R.id.content_background, "setClipToOutline", true); - views.setViewVisibility(R.id.content, View.GONE); + views.setViewVisibility(R.id.text_content, View.GONE); + views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_photo_camera); } else { CharSequence content = tile.getNotificationContent(); views = setPunctuationRemoteViewsFields(views, content); - views.setTextViewText(R.id.content, content); - views.setViewVisibility(R.id.content, View.VISIBLE); + views.setTextViewText(R.id.text_content, tile.getNotificationContent()); + // TODO: Measure max lines from height. + views.setInt(R.id.text_content, "setMaxLines", 2); + TypedValue typedValue = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.textColorPrimary, typedValue, true); + int primaryTextColor = context.getColor(typedValue.resourceId); + views.setInt(R.id.text_content, "setTextColor", primaryTextColor); + views.setViewVisibility(R.id.text_content, View.VISIBLE); views.setViewVisibility(R.id.content_background, View.GONE); + views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_message); } // TODO: Set subtext as Group Sender name once storing the name in PeopleSpaceTile. - views.setTextViewText(R.id.subtext, PeopleSpaceUtils.getLastInteractionString( - context, tile.getLastInteractionTimestamp())); + views.setViewVisibility(R.id.subtext, View.GONE); return views; } private static RemoteViews createLastInteractionRemoteViews(Context context, - PeopleSpaceTile tile) { - RemoteViews views = new RemoteViews( - context.getPackageName(), R.layout.people_space_large_avatar_tile); + PeopleSpaceTile tile, int layoutSize) { + int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view + : R.layout.people_space_large_avatar_tile; + RemoteViews views = new RemoteViews(context.getPackageName(), layout); + if (layoutSize == SMALL_LAYOUT) { + views.setViewVisibility(R.id.name, View.VISIBLE); + views.setViewVisibility(R.id.predefined_icon, View.GONE); + } String status = PeopleSpaceUtils.getLastInteractionString( context, tile.getLastInteractionTimestamp()); views.setTextViewText(R.id.last_interaction, status); @@ -900,18 +992,22 @@ public class PeopleSpaceUtils { removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId); } - /** Updates tile in app widget options and the current view. */ - public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, - Context context, int appWidgetId, PeopleSpaceTile tile) { - AppWidgetOptionsHelper.setPeopleTile(appWidgetManager, appWidgetId, tile); - + private static void updateAppWidgetViews(AppWidgetManager appWidgetManager, + Context context, int appWidgetId, PeopleSpaceTile tile, Bundle options) { if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName()); - RemoteViews views = createRemoteViews(context, tile, appWidgetId); + RemoteViews views = createRemoteViews(context, tile, appWidgetId, options); // Tell the AppWidgetManager to perform an update on the current app widget. appWidgetManager.updateAppWidget(appWidgetId, views); } + /** Updates tile in app widget options and the current view. */ + public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, + Context context, int appWidgetId, PeopleSpaceTile tile) { + Bundle options = AppWidgetOptionsHelper.setPeopleTile(appWidgetManager, appWidgetId, tile); + updateAppWidgetViews(appWidgetManager, context, appWidgetId, tile, options); + } + /** * Returns lookup keys for all contacts with a birthday today. * diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java new file mode 100644 index 000000000000..145fee5e762a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.people; + +import android.annotation.ColorInt; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.IconDrawableFactory; +import android.util.Log; +import android.util.TypedValue; + +import com.android.launcher3.icons.BaseIconFactory; +import com.android.systemui.R; + +class PeopleStoryIconFactory extends BaseIconFactory { + + private static final int PADDING = 2; + private static final int RING_WIDTH = 2; + private static final int MAX_BADGE_SIZE = 36; + + final PackageManager mPackageManager; + final IconDrawableFactory mIconDrawableFactory; + private int mImportantConversationColor; + private int mAccentColor; + private float mDensity; + private float mIconSize; + + PeopleStoryIconFactory(Context context, PackageManager pm, + IconDrawableFactory iconDrawableFactory, int iconSizeDp) { + super(context, context.getResources().getConfiguration().densityDpi, + (int) (iconSizeDp * context.getResources().getDisplayMetrics().density)); + mDensity = context.getResources().getDisplayMetrics().density; + mIconSize = mDensity * iconSizeDp; + mPackageManager = pm; + mIconDrawableFactory = iconDrawableFactory; + mImportantConversationColor = context.getColor(R.color.important_conversation); + TypedValue typedValue = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true); + mAccentColor = context.getColor(typedValue.resourceId); + } + + + /** + * Gets the {@link Drawable} that represents the app icon, badged with the work profile icon + * if appropriate. + */ + private Drawable getAppBadge(String packageName, int userId) { + Drawable badge = null; + try { + final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser( + packageName, PackageManager.GET_META_DATA, userId); + badge = mIconDrawableFactory.getBadgedIcon(appInfo, userId); + } catch (PackageManager.NameNotFoundException e) { + badge = mPackageManager.getDefaultActivityIcon(); + } + return badge; + } + + /** + * Returns a {@link Drawable} for the entire conversation. The shortcut icon will be badged + * with the launcher icon of the app specified by packageName. + */ + public Drawable getPeopleTileDrawable(Drawable headDrawable, String packageName, int userId, + boolean important, boolean newStory) { + return new PeopleStoryIconDrawable(headDrawable, getAppBadge(packageName, userId), + mIconBitmapSize, mImportantConversationColor, important, mIconSize, mDensity, + mAccentColor, newStory); + } + + /** + * Custom drawable which overlays a badge drawable on a head icon (conversation/person avatar), + * with decorations indicating Important conversations and having a New Story. + */ + public static class PeopleStoryIconDrawable extends Drawable { + private float mFullIconSize; + private Drawable mAvatar; + private Drawable mBadgeIcon; + private int mIconSize; + private Paint mPriorityRingPaint; + private boolean mShowImportantRing; + private boolean mShowStoryRing; + private Paint mStoryPaint; + private float mDensity; + + PeopleStoryIconDrawable(Drawable avatar, + Drawable badgeIcon, + int iconSize, + @ColorInt int ringColor, + boolean showImportantRing, float fullIconSize, float density, + @ColorInt int accentColor, boolean showStoryRing) { + mAvatar = avatar; + mBadgeIcon = badgeIcon; + mIconSize = iconSize; + mShowImportantRing = showImportantRing; + mPriorityRingPaint = new Paint(); + mPriorityRingPaint.setStyle(Paint.Style.FILL_AND_STROKE); + mPriorityRingPaint.setColor(ringColor); + mShowStoryRing = showStoryRing; + mStoryPaint = new Paint(); + mStoryPaint.setStyle(Paint.Style.STROKE); + mStoryPaint.setColor(accentColor); + mFullIconSize = fullIconSize; + mDensity = density; + } + + @Override + public int getIntrinsicWidth() { + return mIconSize; + } + + @Override + public int getIntrinsicHeight() { + return mIconSize; + } + + @Override + public void draw(Canvas canvas) { + final Rect bounds = getBounds(); + final int minBound = Math.min(bounds.height(), bounds.width()); + // Scale head icon and app icon to our canvas. + float scale = minBound / mFullIconSize; + + int paddingInDp = (int) (PADDING * mDensity); + int ringStrokeWidth = (int) (RING_WIDTH * mDensity); + mPriorityRingPaint.setStrokeWidth(ringStrokeWidth); + mStoryPaint.setStrokeWidth(ringStrokeWidth); + + int scaledFullIconSize = (int) (mFullIconSize * scale); + int avatarSize = scaledFullIconSize - (paddingInDp * 2); + if (mAvatar != null) { + int leftAndTopPadding = paddingInDp; + int rightAndBottomPadding = avatarSize + paddingInDp; + if (mShowStoryRing) { + int headCenter = scaledFullIconSize / 2; + canvas.drawCircle(headCenter, headCenter, + getRadius(avatarSize, ringStrokeWidth), + mStoryPaint); + leftAndTopPadding += (ringStrokeWidth + paddingInDp); + rightAndBottomPadding -= (ringStrokeWidth + paddingInDp); + } + mAvatar.setBounds(leftAndTopPadding, + leftAndTopPadding, + rightAndBottomPadding, + rightAndBottomPadding); + mAvatar.draw(canvas); + } else { + Log.w("PeopleStoryIconFactory", "Null avatar icon"); + } + + // Determine badge size from either the size relative to the head icon, or max size. + int maxBadgeSize = (int) (MAX_BADGE_SIZE * mDensity); + int badgeSizeRelativeToHead = (int) (avatarSize / 2.4); + int badgeSize = Math.min(maxBadgeSize, badgeSizeRelativeToHead); + if (mBadgeIcon != null) { + int leftAndTopPadding = scaledFullIconSize - badgeSize; + int rightAndBottomPadding = scaledFullIconSize; + if (mShowImportantRing) { + int badgeCenter = leftAndTopPadding + (badgeSize / 2); + canvas.drawCircle(badgeCenter, badgeCenter, + getRadius(badgeSize, ringStrokeWidth), + mPriorityRingPaint); + leftAndTopPadding += ringStrokeWidth; + rightAndBottomPadding -= ringStrokeWidth; + } + mBadgeIcon.setBounds( + leftAndTopPadding, + leftAndTopPadding, + rightAndBottomPadding, + rightAndBottomPadding); + mBadgeIcon.draw(canvas); + } else { + Log.w("PeopleStoryIconFactory", "Null badge icon"); + } + } + + private int getRadius(int circleWidth, int circleStrokeWidth) { + return (circleWidth - circleStrokeWidth) / 2; + } + + @Override + public void setAlpha(int alpha) { + // unimplemented + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + // unimplemented + } + + @Override + public int getOpacity() { + return 0; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java b/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java index df08ee4a42bf..7254eec71d07 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java @@ -37,15 +37,16 @@ public class AppWidgetOptionsHelper { public static final String OPTIONS_PEOPLE_TILE = "options_people_tile"; /** Sets {@link PeopleSpaceTile} in AppWidgetOptions. */ - public static void setPeopleTile(AppWidgetManager appWidgetManager, int appWidgetId, + public static Bundle setPeopleTile(AppWidgetManager appWidgetManager, int appWidgetId, PeopleSpaceTile tile) { + Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); if (tile == null) { if (DEBUG) Log.d(TAG, "Requested to store null tile"); - return; + return options; } - Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); options.putParcelable(OPTIONS_PEOPLE_TILE, tile); appWidgetManager.updateAppWidgetOptions(appWidgetId, options); + return options; } /** Gets {@link PeopleSpaceTile} from AppWidgetOptions. */ diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 7da9a80ca287..30eb2ac160c7 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -217,8 +217,7 @@ public class PeopleSpaceWidgetManager { .setUserName(info.getLabel()) .setUserIcon( PeopleSpaceTile.convertDrawableToIcon(mLauncherApps.getShortcutIconDrawable( - info, 0)) - ) + info, 0))) .setContactUri(uri) .setStatuses(conversation.getStatuses()) .setLastInteractionTimestamp(conversation.getLastEventTimestamp()) @@ -250,9 +249,12 @@ public class PeopleSpaceWidgetManager { } storedTile = storedTile .toBuilder() + // Reset notification content. .setNotificationKey(null) .setNotificationContent(null) .setNotificationDataUri(null) + // Reset missed calls category. + .setNotificationCategory(null) .build(); } updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile); @@ -333,6 +335,8 @@ public class PeopleSpaceWidgetManager { addNewWidget(appWidgetId, optionsKey); AppWidgetOptionsHelper.removePeopleTileKey(mAppWidgetManager, appWidgetId); } + // Update views for new widget dimensions. + updateWidgets(new int[]{appWidgetId}); } /** Adds{@code tile} mapped to {@code appWidgetId}. */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 1411fa1ea21f..5e13fd345b81 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -15,6 +15,8 @@ */ package com.android.systemui.qs; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; + import static com.android.systemui.qs.dagger.QSFragmentModule.QS_SECURITY_FOOTER_VIEW; import android.app.AlertDialog; @@ -244,8 +246,14 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen if (organizationName == null) { return mContext.getString(R.string.quick_settings_disclosure_management); } - return mContext.getString(R.string.quick_settings_disclosure_named_management, - organizationName); + if (isFinancedDevice()) { + return mContext.getString( + R.string.quick_settings_financed_disclosure_named_management, + organizationName); + } else { + return mContext.getString(R.string.quick_settings_disclosure_named_management, + organizationName); + } } // end if(isDeviceManaged) if (hasCACertsInWorkProfile) { if (workProfileOrganizationName == null) { @@ -355,6 +363,10 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen .inflate(R.layout.quick_settings_footer_dialog, null, false); // device management section + TextView deviceManagementSubtitle = + dialogView.findViewById(R.id.device_management_subtitle); + deviceManagementSubtitle.setText(getManagementTitle(deviceOwnerOrganization)); + CharSequence managementMessage = getManagementMessage(isDeviceManaged, deviceOwnerOrganization, isProfileOwnerOfOrganizationOwnedDevice, workProfileOrganizationName); @@ -468,7 +480,8 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen } } - private String getSettingsButton() { + @VisibleForTesting + String getSettingsButton() { return mContext.getString(R.string.monitoring_button_view_policies); } @@ -490,8 +503,13 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen return null; } if (isDeviceManaged && organizationName != null) { - return mContext.getString( - R.string.monitoring_description_named_management, organizationName); + if (isFinancedDevice()) { + return mContext.getString(R.string.monitoring_financed_description_named_management, + organizationName, organizationName); + } else { + return mContext.getString( + R.string.monitoring_description_named_management, organizationName); + } } else if (isProfileOwnerOfOrganizationOwnedDevice && workProfileOrganizationName != null) { return mContext.getString( R.string.monitoring_description_named_management, workProfileOrganizationName); @@ -557,14 +575,23 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen return message; } - private int getTitle(String deviceOwner) { - if (deviceOwner != null) { - return R.string.monitoring_title_device_owned; + @VisibleForTesting + CharSequence getManagementTitle(CharSequence deviceOwnerOrganization) { + if (deviceOwnerOrganization != null && isFinancedDevice()) { + return mContext.getString(R.string.monitoring_title_financed_device, + deviceOwnerOrganization); } else { - return R.string.monitoring_title; + return mContext.getString(R.string.monitoring_title_device_owned); } } + private boolean isFinancedDevice() { + return mSecurityController.isDeviceManaged() + && mSecurityController.getDeviceOwnerType( + mSecurityController.getDeviceOwnerComponentOnAnyUser()) + == DEVICE_OWNER_TYPE_FINANCED; + } + private final Runnable mUpdateIcon = new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index b0a3f437b5ec..8951605846a1 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -25,11 +25,6 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; @@ -40,12 +35,15 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_T import android.annotation.FloatRange; import android.app.ActivityTaskManager; +import android.app.PendingIntent; +import android.app.PictureInPictureParams; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; @@ -59,12 +57,14 @@ import android.os.Looper; import android.os.PatternMatcher; import android.os.RemoteException; import android.os.UserHandle; +import android.util.ArraySet; import android.util.Log; import android.view.InputMonitor; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.accessibility.AccessibilityManager; +import android.window.IRemoteTransition; import androidx.annotation.NonNull; @@ -83,11 +83,15 @@ import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.shared.recents.IOverviewProxy; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; +import com.android.systemui.shared.recents.ISplitScreenListener; +import com.android.systemui.shared.recents.IStartingWindowListener; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.RemoteTransitionCompat; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; @@ -99,7 +103,7 @@ import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; -import com.android.wm.shell.transition.ShellTransitions; +import com.android.wm.shell.transition.RemoteTransitions; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -107,6 +111,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.BiConsumer; +import java.util.function.Consumer; import javax.inject.Inject; @@ -146,11 +151,12 @@ public class OverviewProxyService extends CurrentUserTracker implements private final ScreenshotHelper mScreenshotHelper; private final Optional<OneHanded> mOneHandedOptional; private final CommandQueue mCommandQueue; - private final ShellTransitions mShellTransitions; + private final RemoteTransitions mShellTransitions; private final Optional<StartingSurface> mStartingSurface; private Region mActiveNavBarRegion; + private IPinnedStackAnimationListener mIPinnedStackAnimationListener; private IOverviewProxy mOverviewProxy; private int mConnectionBackoffAttempts; private boolean mBound; @@ -163,6 +169,8 @@ public class OverviewProxyService extends CurrentUserTracker implements private float mWindowCornerRadius; private boolean mSupportsRoundedCornersOnWindows; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; + private final ArraySet<IRemoteTransition> mRemoteTransitions = new ArraySet<>(); + private IStartingWindowListener mIStartingWindowListener; @VisibleForTesting public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() { @@ -375,6 +383,20 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override + public void setShelfHeight(boolean visible, int shelfHeight) { + if (!verifyCaller("setShelfHeight")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mPipOptional.ifPresent( + pip -> pip.setShelfHeight(visible, shelfHeight)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen, Insets visibleInsets, int taskId) { // Deprecated @@ -402,6 +424,36 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + if (!verifyCaller("setPinnedStackAnimationListener")) { + return; + } + mIPinnedStackAnimationListener = listener; + final long token = Binder.clearCallingIdentity(); + try { + mPipOptional.ifPresent( + pip -> pip.setPinnedStackAnimationListener(mPinnedStackAnimationCallback)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setStartingWindowListener(IStartingWindowListener listener) { + if (!verifyCaller("setStartingWindowListener")) { + return; + } + mIStartingWindowListener = listener; + final long token = Binder.clearCallingIdentity(); + try { + mStartingSurface.ifPresent(s -> + s.setStartingWindowListener(mStartingWindowListener)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void onQuickSwitchToNewTask(@Surface.Rotation int rotation) { if (!verifyCaller("onQuickSwitchToNewTask")) { return; @@ -415,6 +467,32 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override + public void startOneHandedMode() { + if (!verifyCaller("startOneHandedMode")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mOneHandedOptional.ifPresent(oneHanded -> oneHanded.startOneHanded()); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void stopOneHandedMode() { + if (!verifyCaller("stopOneHandedMode")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mOneHandedOptional.ifPresent(oneHanded -> oneHanded.stopOneHanded()); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen, Insets visibleInsets, Task.TaskKey task) { mScreenshotHelper.provideScreenshot( @@ -442,6 +520,190 @@ public class OverviewProxyService extends CurrentUserTracker implements } } + @Override + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) { + if (!verifyCaller("startSwipePipToHome")) { + return null; + } + final long binderToken = Binder.clearCallingIdentity(); + try { + return mPipOptional.map(pip -> + pip.startSwipePipToHome(componentName, activityInfo, + pictureInPictureParams, launcherRotation, shelfHeight)) + .orElse(null); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + if (!verifyCaller("stopSwipePipToHome")) { + return; + } + final long binderToken = Binder.clearCallingIdentity(); + try { + mPipOptional.ifPresent(pip -> pip.stopSwipePipToHome( + componentName, destinationBounds)); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) { + if (!verifyCaller("registerRemoteTransition")) return; + final long binderToken = Binder.clearCallingIdentity(); + try { + mRemoteTransitions.add(remoteTransition.getTransition()); + mShellTransitions.registerRemote( + remoteTransition.getFilter(), remoteTransition.getTransition()); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) { + if (!verifyCaller("registerRemoteTransition")) return; + final long binderToken = Binder.clearCallingIdentity(); + try { + mRemoteTransitions.remove(remoteTransition.getTransition()); + mShellTransitions.unregisterRemote(remoteTransition.getTransition()); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void registerSplitScreenListener(ISplitScreenListener listener) { + if (!verifyCaller("registerSplitScreenListener")) { + return; + } + mISplitScreenListener = listener; + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent( + s -> s.registerSplitScreenListener(mSplitScreenListener)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void unregisterSplitScreenListener(ISplitScreenListener listener) { + if (!verifyCaller("unregisterSplitScreenListener")) { + return; + } + mISplitScreenListener = null; + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent( + s -> s.unregisterSplitScreenListener(mSplitScreenListener)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setSideStageVisibility(boolean visible) { + if (!verifyCaller("setSideStageVisibility")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> s.setSideStageVisibility(visible)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void exitSplitScreen() { + if (!verifyCaller("exitSplitScreen")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> s.exitSplitScreen()); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { + if (!verifyCaller("exitSplitScreenOnHide")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> s.exitSplitScreenOnHide(exitSplitScreenOnHide)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void startTask(int taskId, int stage, int position, Bundle options) { + if (!verifyCaller("startTask")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent( + s -> s.startTask(taskId, stage, position, options)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void startShortcut(String packageName, String shortcutId, int stage, int position, + Bundle options, UserHandle user) { + if (!verifyCaller("startShortcut")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> + s.startShortcut(packageName, shortcutId, stage, position, options, user)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void startIntent(PendingIntent intent, Intent fillInIntent, + int stage, int position, Bundle options) { + if (!verifyCaller("startIntent")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> + s.startIntent(intent, mContext, fillInIntent, stage, position, options)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void removeFromSideStage(int taskId) { + if (!verifyCaller("removeFromSideStage")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent( + s -> s.removeFromSideStage(taskId)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { @@ -495,22 +757,6 @@ public class OverviewProxyService extends CurrentUserTracker implements params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder()); params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius); params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows); - - mPipOptional.ifPresent((pip) -> params.putBinder( - KEY_EXTRA_SHELL_PIP, - pip.createExternalInterface().asBinder())); - mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder( - KEY_EXTRA_SHELL_SPLIT_SCREEN, - splitscreen.createExternalInterface().asBinder())); - mOneHandedOptional.ifPresent((onehanded) -> params.putBinder( - KEY_EXTRA_SHELL_ONE_HANDED, - onehanded.createExternalInterface().asBinder())); - params.putBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS, - mShellTransitions.createExternalInterface().asBinder()); - mStartingSurface.ifPresent((startingwindow) -> params.putBinder( - KEY_EXTRA_SHELL_STARTING_WINDOW, - startingwindow.createExternalInterface().asBinder())); - try { mOverviewProxy.onInitialize(params); } catch (RemoteException e) { @@ -550,11 +796,42 @@ public class OverviewProxyService extends CurrentUserTracker implements private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged; private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener = this::notifySplitScreenBoundsChanged; + private final Consumer<Boolean> mPinnedStackAnimationCallback = + this::notifyPinnedStackAnimationStarted; + + private final BiConsumer<Integer, Integer> mStartingWindowListener = + this::notifyTaskLaunching; // This is the death handler for the binder from the launcher service private final IBinder.DeathRecipient mOverviewServiceDeathRcpt = this::cleanupAfterDeath; + private ISplitScreenListener mISplitScreenListener; + private final SplitScreen.SplitScreenListener mSplitScreenListener = + new SplitScreen.SplitScreenListener() { + @Override + public void onStagePositionChanged(int stage, int position) { + try { + if (mISplitScreenListener != null) { + mISplitScreenListener.onStagePositionChanged(stage, position); + } + } catch (RemoteException e) { + Log.e(TAG_OPS, "onStagePositionChanged", e); + } + } + + @Override + public void onTaskStageChanged(int taskId, int stage, boolean visible) { + try { + if (mISplitScreenListener != null) { + mISplitScreenListener.onTaskStageChanged(taskId, stage, visible); + } + } catch (RemoteException e) { + Log.e(TAG_OPS, "onTaskStageChanged", e); + } + } + }; + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Inject public OverviewProxyService(Context context, CommandQueue commandQueue, @@ -567,7 +844,7 @@ public class OverviewProxyService extends CurrentUserTracker implements Optional<Lazy<StatusBar>> statusBarOptionalLazy, Optional<OneHanded> oneHandedOptional, BroadcastDispatcher broadcastDispatcher, - ShellTransitions shellTransitions, + RemoteTransitions shellTransitions, Optional<StartingSurface> startingSurface) { super(broadcastDispatcher); mContext = context; @@ -684,6 +961,29 @@ public class OverviewProxyService extends CurrentUserTracker implements } } + private void notifyPinnedStackAnimationStarted(Boolean isAnimationStarted) { + if (mIPinnedStackAnimationListener == null) { + return; + } + try { + mIPinnedStackAnimationListener.onPinnedStackAnimationStarted(); + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to call onPinnedStackAnimationStarted()", e); + } + } + + private void notifyTaskLaunching(int taskId, int supportedType) { + if (mIStartingWindowListener == null) { + return; + } + + try { + mIStartingWindowListener.onTaskLaunching(taskId, supportedType); + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to call notifyTaskLaunching()", e); + } + } + private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing) { mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING, @@ -727,6 +1027,12 @@ public class OverviewProxyService extends CurrentUserTracker implements // Clean up the minimized state if launcher dies mLegacySplitScreenOptional.ifPresent( splitScreen -> splitScreen.setMinimized(false)); + + // Clean up any registered remote transitions + for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) { + mShellTransitions.unregisterRemote(mRemoteTransitions.valueAt(i)); + } + mRemoteTransitions.clear(); } public void startConnectionToCurrentUser() { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java index bb8c36776d57..1ec175c01be7 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java @@ -26,6 +26,8 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; import android.util.AttributeSet; import android.util.IntArray; import android.util.Log; @@ -94,6 +96,25 @@ public class CropView extends View { } @Override + protected Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + + SavedState ss = new SavedState(superState); + ss.mTopBoundary = getTopBoundary(); + ss.mBottomBoundary = getBottomBoundary(); + return ss; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + + setBoundaryTo(CropBoundary.TOP, ss.mTopBoundary); + setBoundaryTo(CropBoundary.BOTTOM, ss.mBottomBoundary); + } + + @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); float top = mTopCrop + mTopDelta; @@ -380,6 +401,44 @@ public class CropView extends View { */ void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition, int boundaryPositionPx); + } + + static class SavedState extends BaseSavedState { + float mTopBoundary; + float mBottomBoundary; + + /** + * Constructor called from {@link CropView#onSaveInstanceState()} + */ + SavedState(Parcelable superState) { + super(superState); + } + + /** + * Constructor called from {@link #CREATOR} + */ + private SavedState(Parcel in) { + super(in); + mTopBoundary = in.readFloat(); + mBottomBoundary = in.readFloat(); + } + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeFloat(mTopBoundary); + out.writeFloat(mBottomBoundary); + } + + public static final Parcelable.Creator<SavedState> CREATOR + = new Parcelable.Creator<SavedState>() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java index bc8adc9dad5b..4aead817fe8c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java @@ -213,6 +213,20 @@ class ImageExporter { CompressFormat format; boolean published; boolean deleted; + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Result{"); + sb.append("uri=").append(uri); + sb.append(", requestId=").append(requestId); + sb.append(", fileName='").append(fileName).append('\''); + sb.append(", timestamp=").append(timestamp); + sb.append(", format=").append(format); + sb.append(", published=").append(published); + sb.append(", deleted=").append(deleted); + sb.append('}'); + return sb.toString(); + } } private static class Task { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java index 988b93c8ca59..7ee7c319799c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java @@ -21,7 +21,6 @@ import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; -import android.os.ParcelFileDescriptor; import androidx.concurrent.futures.CallbackToFutureAdapter; @@ -43,6 +42,16 @@ public class ImageLoader { @Nullable Uri uri; @Nullable File fileName; @Nullable Bitmap bitmap; + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Result{"); + sb.append("uri=").append(uri); + sb.append(", fileName=").append(fileName); + sb.append(", bitmap=").append(bitmap); + sb.append('}'); + return sb.toString(); + } } @Inject @@ -54,7 +63,7 @@ public class ImageLoader { * Loads an image via URI from ContentResolver. * * @param uri the identifier of the image to load - * @return a listenable future result + * @return a listenable future result containing a bitmap */ ListenableFuture<Result> load(Uri uri) { return CallbackToFutureAdapter.getFuture(completer -> { @@ -76,7 +85,7 @@ public class ImageLoader { * permissions to read this file/path. * * @param file the system file path of the image to load - * @return a listenable future result + * @return a listenable future result containing a bitmap */ ListenableFuture<Result> load(File file) { return CallbackToFutureAdapter.getFuture(completer -> { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java index 6743afa3ab59..07adc7bd7053 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java @@ -31,6 +31,7 @@ import com.android.internal.util.CallbackRegistry; import com.android.internal.util.CallbackRegistry.NotifierCallback; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -69,9 +70,6 @@ class ImageTileSet { private final Rect mBounds = new Rect(); private final Handler mHandler; - private OnContentChangedListener mOnContentChangedListener; - private OnBoundsChangedListener mOnBoundsChangedListener; - void addOnBoundsChangedListener(OnBoundsChangedListener listener) { if (mOnBoundsListeners == null) { mOnBoundsListeners = new CallbackRegistry<>( @@ -204,18 +202,17 @@ class ImageTileSet { return mBounds.height(); } - @AnyThread void clear() { - if (!mHandler.getLooper().isCurrentThread()) { - mHandler.post(this::clear); - return; - } if (mTiles.isEmpty()) { return; } mBounds.setEmpty(); - mTiles.forEach(ImageTile::close); - mTiles.clear(); + Iterator<ImageTile> i = mTiles.iterator(); + while (i.hasNext()) { + ImageTile next = i.next(); + next.close(); + i.remove(); + } notifyBoundsChanged(mBounds); notifyContentChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index db997053af23..3ac884b98136 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -21,7 +21,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.HardwareRenderer; import android.graphics.RecordingCanvas; import android.graphics.Rect; @@ -30,10 +29,13 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; -import android.os.SystemClock; +import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import android.view.IScrollCaptureConnection; +import android.view.IWindowManager; +import android.view.ScrollCaptureResponse; import android.view.View; import android.widget.ImageView; @@ -41,14 +43,14 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot; import com.google.common.util.concurrent.ListenableFuture; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.time.ZonedDateTime; import java.util.UUID; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -61,18 +63,15 @@ import javax.inject.Inject; public class LongScreenshotActivity extends Activity { private static final String TAG = "LongScreenshotActivity"; - private static final String IMAGE_PATH_KEY = "saved-image"; - private static final String TOP_BOUNDARY_KEY = "top-boundary"; - private static final String BOTTOM_BOUNDARY_KEY = "bottom-boundary"; + public static final String EXTRA_CAPTURE_RESPONSE = "capture-response"; + private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path"; private final UiEventLogger mUiEventLogger; private final ScrollCaptureController mScrollCaptureController; - private final ScrollCaptureClient.Connection mConnection; private final Executor mUiExecutor; private final Executor mBackgroundExecutor; private final ImageExporter mImageExporter; - private String mSavedImagePath; // If true, the activity is re-loading an image from storage, which should either succeed and // populate the UI or fail and finish the activity. private boolean mRestoringInstance; @@ -84,6 +83,14 @@ public class LongScreenshotActivity extends Activity { private View mShare; private CropView mCropView; private MagnifierView mMagnifierView; + private ScrollCaptureResponse mScrollCaptureResponse; + private File mSavedImagePath; + + private ListenableFuture<File> mCacheSaveFuture; + private ListenableFuture<ImageLoader.Result> mCacheLoadFuture; + + private ListenableFuture<LongScreenshot> mLongScreenshotFuture; + private LongScreenshot mLongScreenshot; private enum PendingAction { SHARE, @@ -92,35 +99,31 @@ public class LongScreenshotActivity extends Activity { } @Inject - public LongScreenshotActivity(UiEventLogger uiEventLogger, - ImageExporter imageExporter, - @Main Executor mainExecutor, - @Background Executor bgExecutor, + public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter, + @Main Executor mainExecutor, @Background Executor bgExecutor, IWindowManager wms, Context context) { mUiEventLogger = uiEventLogger; mUiExecutor = mainExecutor; mBackgroundExecutor = bgExecutor; mImageExporter = imageExporter; - - mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor, - imageExporter); - - mConnection = ScreenshotController.takeScrollCaptureConnection(); + mScrollCaptureController = new ScrollCaptureController(context, bgExecutor, wms); } + @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")"); + super.onCreate(savedInstanceState); setContentView(R.layout.long_screenshot); - mPreview = findViewById(R.id.preview); - mSave = findViewById(R.id.save); - mCancel = findViewById(R.id.cancel); - mEdit = findViewById(R.id.edit); - mShare = findViewById(R.id.share); - mCropView = findViewById(R.id.crop_view); - mMagnifierView = findViewById(R.id.magnifier); + mPreview = requireViewById(R.id.preview); + mSave = requireViewById(R.id.save); + mCancel = requireViewById(R.id.cancel); + mEdit = requireViewById(R.id.edit); + mShare = requireViewById(R.id.share); + mCropView = requireViewById(R.id.crop_view); + mMagnifierView = requireViewById(R.id.magnifier); mCropView.setCropInteractionListener(mMagnifierView); mSave.setOnClickListener(this::onClicked); @@ -128,71 +131,188 @@ public class LongScreenshotActivity extends Activity { mEdit.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); - if (savedInstanceState != null) { - String imagePath = savedInstanceState.getString(IMAGE_PATH_KEY); - if (!TextUtils.isEmpty(imagePath)) { - mRestoringInstance = true; - mBackgroundExecutor.execute(() -> { - Bitmap bitmap = BitmapFactory.decodeFile(imagePath); - if (bitmap == null) { - Log.e(TAG, "Failed to read bitmap from " + imagePath); - finishAndRemoveTask(); - } else { - runOnUiThread(() -> { - BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); - mPreview.setImageDrawable(drawable); - mMagnifierView.setDrawable(drawable, bitmap.getWidth(), - bitmap.getHeight()); - - mCropView.setBoundaryTo(CropView.CropBoundary.TOP, - savedInstanceState.getFloat(TOP_BOUNDARY_KEY, 0f)); - mCropView.setBoundaryTo(CropView.CropBoundary.BOTTOM, - savedInstanceState.getFloat(BOTTOM_BOUNDARY_KEY, 1f)); - mRestoringInstance = false; - // Reuse the same path for subsequent restoration. - mSavedImagePath = imagePath; - Log.d(TAG, "Loaded bitmap from " + imagePath); - }); - } - }); - } - } mPreview.addOnLayoutChangeListener( (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> updateCropLocation()); + + Intent intent = getIntent(); + mScrollCaptureResponse = intent.getParcelableExtra(EXTRA_CAPTURE_RESPONSE); + + if (savedInstanceState != null) { + String savedImagePath = savedInstanceState.getString(KEY_SAVED_IMAGE_PATH); + if (savedImagePath == null) { + Log.e(TAG, "Missing saved state entry with key '" + KEY_SAVED_IMAGE_PATH + "'!"); + finishAndRemoveTask(); + return; + } + mSavedImagePath = new File(savedImagePath); + ImageLoader imageLoader = new ImageLoader(getContentResolver()); + mCacheLoadFuture = imageLoader.load(mSavedImagePath); + } } @Override public void onStart() { + Log.d(TAG, "onStart"); super.onStart(); - if (mPreview.getDrawable() == null && !mRestoringInstance) { - if (mConnection == null) { - Log.e(TAG, "Failed to get scroll capture connection, bailing out"); + + if (mCacheLoadFuture != null) { + Log.d(TAG, "mRestoringInstance = true"); + final ListenableFuture<ImageLoader.Result> future = mCacheLoadFuture; + mCacheLoadFuture.addListener(() -> { + Log.d(TAG, "cached bitmap load complete"); + try { + onCachedImageLoaded(future.get()); + } catch (CancellationException | ExecutionException | InterruptedException e) { + Log.e(TAG, "Failed to load cached image", e); + if (mSavedImagePath != null) { + //noinspection ResultOfMethodCallIgnored + mSavedImagePath.delete(); + mSavedImagePath = null; + } + finishAndRemoveTask(); + } + }, mUiExecutor); + mCacheLoadFuture = null; + return; + } + + if (mLongScreenshotFuture == null) { + Log.d(TAG, "mLongScreenshotFuture == null"); + // First run through, ensure we have a connection to use (see #onCreate) + if (mScrollCaptureResponse == null || !mScrollCaptureResponse.isConnected()) { + Log.e(TAG, "Did not receive a live scroll capture connection, bailing out!"); finishAndRemoveTask(); return; } - doCapture(); + mLongScreenshotFuture = mScrollCaptureController.run(mScrollCaptureResponse); + mLongScreenshotFuture.addListener(() -> { + LongScreenshot longScreenshot; + try { + longScreenshot = mLongScreenshotFuture.get(); + } catch (CancellationException | InterruptedException | ExecutionException e) { + Log.e(TAG, "Error capturing long screenshot!", e); + finishAndRemoveTask(); + return; + } + if (longScreenshot.getHeight() == 0) { + Log.e(TAG, "Got a zero height result"); + finishAndRemoveTask(); + return; + } + onCaptureCompleted(longScreenshot); + }, mUiExecutor); + } else { + Log.d(TAG, "mLongScreenshotFuture != null"); } } + private void onCaptureCompleted(LongScreenshot longScreenshot) { + Log.d(TAG, "onCaptureCompleted(longScreenshot=" + longScreenshot + ")"); + mLongScreenshot = longScreenshot; + mPreview.setImageDrawable(mLongScreenshot.getDrawable()); + updateCropLocation(); + mMagnifierView.setDrawable(mLongScreenshot.getDrawable(), + mLongScreenshot.getWidth(), mLongScreenshot.getHeight()); + // Original boundaries go from the image tile set's y=0 to y=pageSize, so + // we animate to that as a starting crop position. + float topFraction = Math.max(0, + -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight()); + float bottomFraction = Math.min(1f, + 1 - (mLongScreenshot.getBottom() - mLongScreenshot.getPageHeight()) + / (float) mLongScreenshot.getHeight()); + mCropView.animateBoundaryTo(CropView.CropBoundary.TOP, topFraction); + mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, bottomFraction); + setButtonsEnabled(true); + + // Immediately export to temp image file for saved state + mCacheSaveFuture = mImageExporter.exportAsTempFile(mBackgroundExecutor, + mLongScreenshot.toBitmap()); + mCacheSaveFuture.addListener(() -> { + try { + // Get the temp file path to persist, used in onSavedInstanceState + mSavedImagePath = mCacheSaveFuture.get(); + } catch (CancellationException | InterruptedException | ExecutionException e) { + Log.e(TAG, "Error saving temp image file", e); + finishAndRemoveTask(); + } + }, mUiExecutor); + } + + private void onCachedImageLoaded(ImageLoader.Result imageResult) { + Log.d(TAG, "onCachedImageLoaded(imageResult=" + imageResult + ")"); + BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap); + mPreview.setImageDrawable(drawable); + mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(), + imageResult.bitmap.getHeight()); + mSavedImagePath = imageResult.fileName; + + setButtonsEnabled(true); + } + + private static Bitmap renderBitmap(Drawable drawable, Rect bounds) { + final RenderNode output = new RenderNode("Bitmap Export"); + output.setPosition(0, 0, bounds.width(), bounds.height()); + RecordingCanvas canvas = output.beginRecording(); + canvas.translate(-bounds.left, -bounds.top); + canvas.clipRect(bounds); + drawable.draw(canvas); + output.endRecording(); + return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height()); + } + @Override protected void onSaveInstanceState(Bundle outState) { + Log.d(TAG, "onSaveInstanceState"); super.onSaveInstanceState(outState); - outState.putString(IMAGE_PATH_KEY, mSavedImagePath); - outState.putFloat(TOP_BOUNDARY_KEY, mCropView.getTopBoundary()); - outState.putFloat(BOTTOM_BOUNDARY_KEY, mCropView.getBottomBoundary()); + if (mSavedImagePath != null) { + outState.putString(KEY_SAVED_IMAGE_PATH, mSavedImagePath.getPath()); + } } @Override - protected void onDestroy() { - super.onDestroy(); - if (isFinishing() && !TextUtils.isEmpty(mSavedImagePath)) { + protected void onPause() { + Log.d(TAG, "onPause finishing=" + isFinishing()); + super.onPause(); + if (isFinishing()) { + if (mScrollCaptureResponse != null) { + mScrollCaptureResponse.close(); + } + cleanupCache(); + + if (mLongScreenshotFuture != null) { + mLongScreenshotFuture.cancel(true); + } + if (mLongScreenshot != null) { + mLongScreenshot.release(); + } + } + } + + void cleanupCache() { + if (mCacheSaveFuture != null) { + mCacheSaveFuture.cancel(true); + } + if (mSavedImagePath != null) { Log.d(TAG, "Deleting " + mSavedImagePath); - File file = new File(mSavedImagePath); - file.delete(); + //noinspection ResultOfMethodCallIgnored + mSavedImagePath.delete(); + mSavedImagePath = null; } } + @Override + protected void onStop() { + Log.d(TAG, "onStop"); + super.onStop(); + } + + @Override + protected void onDestroy() { + Log.d(TAG, "onDestroy"); + super.onDestroy(); + } + private void setButtonsEnabled(boolean enabled) { mSave.setEnabled(enabled); mCancel.setEnabled(enabled); @@ -244,67 +364,51 @@ public class LongScreenshotActivity extends Activity { } private void startExport(PendingAction action) { + Log.d(TAG, "startExport(action = " + action + ")"); Drawable drawable = mPreview.getDrawable(); + if (drawable == null) { + Log.e(TAG, "No drawable, skipping export!"); + return; + } - Rect croppedPortion = new Rect( - 0, - (int) (drawable.getIntrinsicHeight() * mCropView.getTopBoundary()), - drawable.getIntrinsicWidth(), - (int) (drawable.getIntrinsicHeight() * mCropView.getBottomBoundary())); - ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( - mBackgroundExecutor, UUID.randomUUID(), getBitmap(croppedPortion, drawable), - ZonedDateTime.now()); - exportFuture.addListener(() -> { - try { - ImageExporter.Result result = exportFuture.get(); - setButtonsEnabled(true); - switch (action) { - case EDIT: - doEdit(result.uri); - break; - case SHARE: - doShare(result.uri); - break; - case SAVE: - // Nothing more to do - finishAndRemoveTask(); - break; - } - } catch (InterruptedException | ExecutionException e) { - Log.e(TAG, "failed to export", e); - setButtonsEnabled(true); - } - }, mUiExecutor); - } + Rect bounds = new Rect(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + int height = bounds.height(); + bounds.top = (int) (height * mCropView.getTopBoundary()); + bounds.bottom = (int) (height * mCropView.getBottomBoundary()); - private Bitmap getBitmap(Rect bounds, Drawable drawable) { - final RenderNode output = new RenderNode("Bitmap Export"); - output.setPosition(0, 0, bounds.width(), bounds.height()); - RecordingCanvas canvas = output.beginRecording(); - // Translating the canvas instead of setting drawable bounds since the drawable is still - // used in the preview. - canvas.translate(0, -bounds.top); - drawable.draw(canvas); - output.endRecording(); - return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height()); + if (bounds.isEmpty()) { + Log.w(TAG, "Crop bounds empty, skipping export."); + return; + } + + Bitmap output = renderBitmap(mPreview.getDrawable(), bounds); + ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( + mBackgroundExecutor, UUID.randomUUID(), output, ZonedDateTime.now()); + exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor); } - private void saveCacheBitmap(ImageTileSet tileSet) { - long startTime = SystemClock.uptimeMillis(); - Bitmap bitmap = tileSet.toBitmap(); - // TODO(b/181562529) Remove this - mPreview.setImageDrawable(tileSet.getDrawable()); + private void onExportCompleted(PendingAction action, + ListenableFuture<ImageExporter.Result> exportFuture) { + setButtonsEnabled(true); + ImageExporter.Result result; try { - File file = File.createTempFile("long_screenshot", ".png", null); - FileOutputStream stream = new FileOutputStream(file); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); - stream.flush(); - stream.close(); - mSavedImagePath = file.getAbsolutePath(); - Log.d(TAG, "Saved to " + file.getAbsolutePath() + " in " - + (SystemClock.uptimeMillis() - startTime) + "ms"); - } catch (IOException e) { - Log.e(TAG, "Failed to save bitmap", e); + result = exportFuture.get(); + } catch (CancellationException | InterruptedException | ExecutionException e) { + Log.e(TAG, "failed to export", e); + return; + } + + switch (action) { + case EDIT: + doEdit(result.uri); + break; + case SHARE: + doShare(result.uri); + break; + case SAVE: + // Nothing more to do + finishAndRemoveTask(); + break; } } @@ -313,8 +417,8 @@ public class LongScreenshotActivity extends Activity { if (drawable == null) { return; } - - float imageRatio = drawable.getBounds().width() / (float) drawable.getBounds().height(); + Rect bounds = drawable.getBounds(); + float imageRatio = bounds.width() / (float) bounds.height(); float viewRatio = mPreview.getWidth() / (float) mPreview.getHeight(); if (imageRatio > viewRatio) { @@ -328,35 +432,4 @@ public class LongScreenshotActivity extends Activity { mCropView.setExtraPadding(0, 0); } } - - private void doCapture() { - mScrollCaptureController.start(mConnection, - new ScrollCaptureController.ScrollCaptureCallback() { - @Override - public void onError() { - Log.e(TAG, "Error capturing long screenshot!"); - finishAndRemoveTask(); - } - - @Override - public void onComplete(ImageTileSet imageTileSet, int pageSize) { - Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x " - + imageTileSet.getHeight()); - mPreview.setImageDrawable(imageTileSet.getDrawable()); - updateCropLocation(); - mMagnifierView.setDrawable(imageTileSet.getDrawable(), - imageTileSet.getWidth(), imageTileSet.getHeight()); - // Original boundaries go from the image tile set's y=0 to y=pageSize, so - // we animate to that as a starting crop position. - float topFraction = Math.max(0, - -imageTileSet.getTop() / (float) imageTileSet.getHeight()); - float bottomFraction = Math.min(1f, - 1 - (imageTileSet.getBottom() - pageSize) - / (float) imageTileSet.getHeight()); - mCropView.animateBoundaryTo(CropView.CropBoundary.TOP, topFraction); - mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, bottomFraction); - mBackgroundExecutor.execute(() -> saveCacheBitmap(imageTileSet)); - } - }); - } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 3d6dea3cd3f0..798a063f15b9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -65,6 +65,7 @@ import android.view.DisplayAddress; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.ScrollCaptureResponse; import android.view.SurfaceControl; import android.view.View; import android.view.ViewTreeObserver; @@ -86,10 +87,14 @@ import com.android.systemui.screenshot.ScreenshotController.SavedImageData.Actio import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback; import com.android.systemui.util.DeviceConfigProxy; +import com.google.common.util.concurrent.ListenableFuture; + import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.function.Supplier; @@ -101,7 +106,8 @@ import javax.inject.Inject; public class ScreenshotController { private static final String TAG = logTag(ScreenshotController.class); - private static ScrollCaptureClient.Connection sScrollConnection; + private ScrollCaptureResponse mLastScrollCaptureResponse; + private ListenableFuture<ScrollCaptureResponse> mLastScrollCaptureRequest; /** * POD used in the AsyncTask which saves an image in the background. @@ -222,12 +228,6 @@ public class ScreenshotController { | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS); - public static @Nullable ScrollCaptureClient.Connection takeScrollCaptureConnection() { - ScrollCaptureClient.Connection connection = sScrollConnection; - sScrollConnection = null; - return connection; - } - @Inject ScreenshotController( Context context, @@ -352,6 +352,11 @@ public class ScreenshotController { } else { mScreenshotView.animateDismissal(); } + + if (mLastScrollCaptureResponse != null) { + mLastScrollCaptureResponse.close(); + mLastScrollCaptureResponse = null; + } } boolean isPendingSharedTransition() { @@ -526,16 +531,15 @@ public class ScreenshotController { // the reference used to locate the target window (below). withWindowAttached(() -> { mScrollCaptureClient.setHostWindowToken(decorView.getWindowToken()); - mScrollCaptureClient.request(DEFAULT_DISPLAY, - /* onConnection */ - (connection) -> mScreenshotHandler.post(() -> - mScreenshotView.showScrollChip(() -> - /* onClick */ - runScrollCapture(connection)))); + if (mLastScrollCaptureRequest != null) { + mLastScrollCaptureRequest.cancel(true); + } + mLastScrollCaptureRequest = mScrollCaptureClient.request(DEFAULT_DISPLAY); + mLastScrollCaptureRequest.addListener(() -> + onScrollCaptureResponseReady(mLastScrollCaptureRequest), mMainExecutor); }); } - attachWindow(); mScreenshotView.getViewTreeObserver().addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener() { @@ -560,6 +564,27 @@ public class ScreenshotController { cancelTimeout(); // restarted after animation } + private void onScrollCaptureResponseReady(Future<ScrollCaptureResponse> responseFuture) { + try { + if (mLastScrollCaptureResponse != null) { + mLastScrollCaptureResponse.close(); + } + mLastScrollCaptureResponse = responseFuture.get(); + final Intent intent = new Intent(mContext, LongScreenshotActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(LongScreenshotActivity.EXTRA_CAPTURE_RESPONSE, + mLastScrollCaptureResponse); + mScreenshotView.showScrollChip(/* onClick */ () -> { + // Clear the reference to prevent close() in dismissScreenshot + mLastScrollCaptureResponse = null; + mContext.startActivity(intent); + dismissScreenshot(false); + }); + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "requestScrollCapture failed", e); + } + } + private void withWindowAttached(Runnable action) { View decorView = mWindow.getDecorView(); if (decorView.isAttachedToWindow()) { @@ -606,15 +631,6 @@ public class ScreenshotController { } } - private void runScrollCapture(ScrollCaptureClient.Connection connection) { - sScrollConnection = connection; // For LongScreenshotActivity to pick up. - - Intent intent = new Intent(mContext, LongScreenshotActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivity(intent); - dismissScreenshot(false); - } - /** * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on * failure). diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java index 54b99bbe74cc..926d5c4701aa 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,18 +30,23 @@ import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.media.Image; import android.media.ImageReader; +import android.os.DeadObjectException; import android.os.IBinder; import android.os.ICancellationSignal; import android.os.RemoteException; import android.util.Log; import android.view.IScrollCaptureCallbacks; import android.view.IScrollCaptureConnection; +import android.view.IScrollCaptureResponseListener; import android.view.IWindowManager; import android.view.ScrollCaptureResponse; +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.concurrent.futures.CallbackToFutureAdapter.Completer; + import com.android.internal.annotations.VisibleForTesting; -import java.util.function.Consumer; +import com.google.common.util.concurrent.ListenableFuture; import javax.inject.Inject; @@ -57,61 +62,6 @@ public class ScrollCaptureClient { private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class); - - /** - * A connection to a remote window. Starts a capture session. - */ - public interface Connection { - /** - * Start a session. - - * @param sessionConsumer listener to receive the session once active - * @param maxPages the capture buffer size expressed as a multiple of the content height - */ - // TODO ListenableFuture - void start(Consumer<Session> sessionConsumer, float maxPages); - - /** - * Close the connection. Must end capture if started to avoid potential unwanted visual - * artifacts. - * - * @see Session#end(Runnable) - */ - void close(); - } - - static class CaptureResult { - public final Image image; - /** - * The area requested, in content rect space, relative to scroll-bounds. - */ - public final Rect requested; - /** - * The actual area captured, in content rect space, relative to scroll-bounds. This may be - * cropped or empty depending on available content. - */ - public final Rect captured; - - // Error? - - private CaptureResult(Image image, Rect request, Rect captured) { - this.image = image; - this.requested = request; - this.captured = captured; - } - - @Override - public String toString() { - return "CaptureResult{" - + "requested=" + requested - + " (" + requested.width() + "x" + requested.height() + ")" - + ", captured=" + captured - + " (" + captured.width() + "x" + captured.height() + ")" - + ", image=" + image - + '}'; - } - } - /** * Represents the connection to a target window and provides a mechanism for requesting tiles. */ @@ -121,10 +71,8 @@ public class ScrollCaptureClient { * and from left 0, to {@link #getPageWidth()} * * @param top the top (y) position of the tile to capture, in content rect space - * @param consumer listener to be informed of the result */ - // TODO ListenableFuture - void requestTile(int top, Consumer<CaptureResult> consumer); + ListenableFuture<CaptureResult> requestTile(int top); /** * Returns the maximum number of tiles which may be requested and retained without @@ -139,6 +87,7 @@ public class ScrollCaptureClient { */ int getTileHeight(); + /** * @return the height of scrollable content being captured */ @@ -155,11 +104,42 @@ public class ScrollCaptureClient { Rect getWindowBounds(); /** - * End the capture session, return the target app to original state. The listener - * will be called when the target app is ready to before visible and interactive. + * End the capture session, return the target app to original state. The returned Future + * will complete once the target app is ready to become visible and interactive. */ - // TODO ListenableFuture - void end(Runnable listener); + ListenableFuture<Void> end(); + + void release(); + } + + static class CaptureResult { + public final Image image; + /** + * The area requested, in content rect space, relative to scroll-bounds. + */ + public final Rect requested; + /** + * The actual area captured, in content rect space, relative to scroll-bounds. This may be + * cropped or empty depending on available content. + */ + public final Rect captured; + + CaptureResult(Image image, Rect request, Rect captured) { + this.image = image; + this.requested = request; + this.captured = captured; + } + + @Override + public String toString() { + return "CaptureResult{" + + "requested=" + requested + + " (" + requested.width() + "x" + requested.height() + ")" + + ", captured=" + captured + + " (" + captured.width() + "x" + captured.height() + ")" + + ", image=" + image + + '}'; + } } private final IWindowManager mWindowManagerService; @@ -185,10 +165,9 @@ public class ScrollCaptureClient { * Check for scroll capture support. * * @param displayId id for the display containing the target window - * @param consumer receives a connection when available */ - public void request(int displayId, Consumer<Connection> consumer) { - request(displayId, MATCH_ANY_TASK, consumer); + public ListenableFuture<ScrollCaptureResponse> request(int displayId) { + return request(displayId, MATCH_ANY_TASK); } /** @@ -196,194 +175,209 @@ public class ScrollCaptureClient { * * @param displayId id for the display containing the target window * @param taskId id for the task containing the target window or {@link #MATCH_ANY_TASK}. - * @param consumer receives a connection when available + * @return a listenable future providing the response */ - public void request(int displayId, int taskId, Consumer<Connection> consumer) { - try { - if (DEBUG_SCROLL) { - Log.d(TAG, "requestScrollCapture(displayId=" + displayId + ", " + mHostWindowToken - + ", taskId=" + taskId + ", consumer=" + consumer + ")"); + public ListenableFuture<ScrollCaptureResponse> request(int displayId, int taskId) { + return CallbackToFutureAdapter.getFuture((completer) -> { + try { + mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId, + new IScrollCaptureResponseListener.Stub() { + @Override + public void onScrollCaptureResponse(ScrollCaptureResponse response) { + completer.set(response); + } + }); + + } catch (RemoteException e) { + completer.setException(e); } - mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId, - new ClientCallbacks(consumer)); - } catch (RemoteException e) { - Log.e(TAG, "Ignored remote exception", e); - } + return "ScrollCaptureClient#request" + + "(displayId=" + displayId + ", taskId=" + taskId + ")"; + }); + } + + /** + * Start a scroll capture session. + * + * @param response a response provided from a request containing a connection + * @param maxPages the capture buffer size expressed as a multiple of the content height + * @return a listenable future providing the session + */ + public ListenableFuture<Session> start(ScrollCaptureResponse response, float maxPages) { + IScrollCaptureConnection connection = response.getConnection(); + return CallbackToFutureAdapter.getFuture((completer) -> { + if (connection == null || !connection.asBinder().isBinderAlive()) { + completer.setException(new DeadObjectException("No active connection!")); + return ""; + } + SessionWrapper session = new SessionWrapper(connection, response.getWindowBounds(), + response.getBoundsInWindow(), maxPages); + session.start(completer); + return "IScrollCaptureCallbacks#onCaptureStarted"; + }); } - private static class ClientCallbacks extends IScrollCaptureCallbacks.Stub implements - Connection, Session, IBinder.DeathRecipient { + private static class SessionWrapper extends IScrollCaptureCallbacks.Stub implements Session, + IBinder.DeathRecipient { private IScrollCaptureConnection mConnection; - private Consumer<Connection> mConnectionConsumer; - private Consumer<Session> mSessionConsumer; - private Consumer<CaptureResult> mResultConsumer; - private Runnable mShutdownListener; private ImageReader mReader; - private Rect mScrollBounds; - private int mTileHeight; - private int mTileWidth; + private final int mTileHeight; + private final int mTileWidth; private Rect mRequestRect; private boolean mStarted; private ICancellationSignal mCancellationSignal; - private Rect mWindowBounds; - private Rect mBoundsInWindow; - private int mMaxTiles; + private final Rect mWindowBounds; + private final Rect mBoundsInWindow; + private final int mMaxTiles; + + private Completer<Session> mStartCompleter; + private Completer<CaptureResult> mTileRequestCompleter; + private Completer<Void> mEndCompleter; + + private SessionWrapper(IScrollCaptureConnection connection, Rect windowBounds, + Rect boundsInWindow, float maxPages) throws RemoteException { + mConnection = requireNonNull(connection); + mConnection.asBinder().linkToDeath(SessionWrapper.this, 0); + mWindowBounds = requireNonNull(windowBounds); + mBoundsInWindow = requireNonNull(boundsInWindow); + + int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height(); + int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE)); + + mTileWidth = mBoundsInWindow.width(); + mTileHeight = pxPerTile / mBoundsInWindow.width(); + mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE); - private ClientCallbacks(Consumer<Connection> connectionConsumer) { - mConnectionConsumer = connectionConsumer; + if (DEBUG_SCROLL) { + Log.d(TAG, "boundsInWindow: " + mBoundsInWindow); + Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight); + } } - @BinderThread @Override - public void onScrollCaptureResponse(ScrollCaptureResponse response) throws RemoteException { - if (DEBUG_SCROLL) { - Log.d(TAG, "onScrollCaptureResponse(response=" + response + ")"); + public void binderDied() { + Log.d(TAG, "binderDied! The target process just crashed :-("); + // Clean up + mConnection = null; + + // Pass along the bad news. + if (mStartCompleter != null) { + mStartCompleter.setException(new DeadObjectException("The remote process died")); } - if (response.isConnected()) { - mConnection = response.getConnection(); - mConnection.asBinder().linkToDeath(this, 0); - mWindowBounds = response.getWindowBounds(); - mBoundsInWindow = response.getBoundsInWindow(); - - int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height(); - int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE)); - mTileWidth = mBoundsInWindow.width(); - mTileHeight = pxPerTile / mBoundsInWindow.width(); - if (DEBUG_SCROLL) { - Log.d(TAG, "boundsInWindow: " + mBoundsInWindow); - Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight); - Log.d(TAG, "maxHeight: " + (mMaxTiles * mTileHeight) + "px"); - } - mConnectionConsumer.accept(this); + if (mTileRequestCompleter != null) { + mTileRequestCompleter.setException( + new DeadObjectException("The remote process died")); + } + if (mEndCompleter != null) { + mEndCompleter.setException(new DeadObjectException("The remote process died")); } - mConnectionConsumer = null; } - @Override - public void start(Consumer<Session> sessionConsumer, float maxPages) { - if (DEBUG_SCROLL) { - Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + "," - + " maxPages=" + maxPages + ")"); - } - mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE); + private void start(Completer<Session> completer) { mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888, mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); - mSessionConsumer = sessionConsumer; - + mStartCompleter = completer; try { - mCancellationSignal = mConnection.startCapture(mReader.getSurface()); + mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this); + completer.addCancellationListener(() -> { + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + // Ignore + } + }, Runnable::run); mStarted = true; } catch (RemoteException e) { - Log.w(TAG, "Failed to start", e); mReader.close(); + completer.setException(e); } } @BinderThread @Override public void onCaptureStarted() { - if (DEBUG_SCROLL) { - Log.d(TAG, "onCaptureStarted()"); - } - mSessionConsumer.accept(this); - mSessionConsumer = null; + Log.d(TAG, "onCaptureStarted"); + mStartCompleter.set(this); } @Override - public void requestTile(int top, Consumer<CaptureResult> consumer) { - if (DEBUG_SCROLL) { - Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")"); - } - cancelPendingRequest(); + public ListenableFuture<CaptureResult> requestTile(int top) { mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight); - mResultConsumer = consumer; - try { - mCancellationSignal = mConnection.requestImage(mRequestRect); - } catch (RemoteException e) { - Log.e(TAG, "Caught remote exception from requestImage", e); - } + return CallbackToFutureAdapter.getFuture((completer -> { + if (mConnection == null || !mConnection.asBinder().isBinderAlive()) { + completer.setException(new DeadObjectException("Connection is closed!")); + return ""; + } + try { + mTileRequestCompleter = completer; + mCancellationSignal = mConnection.requestImage(mRequestRect); + completer.addCancellationListener(() -> { + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + // Ignore + } + }, Runnable::run); + } catch (RemoteException e) { + completer.setException(e); + } + return "IScrollCaptureCallbacks#onImageRequestCompleted"; + })); } + @BinderThread @Override public void onImageRequestCompleted(int flags, Rect contentArea) { Image image = mReader.acquireLatestImage(); - if (DEBUG_SCROLL) { - Log.d(TAG, "onCaptureBufferSent(flags=" + flags - + ", contentArea=" + contentArea + ") image=" + image); - } - // Save and clear first, since the consumer will likely request the next - // tile, otherwise the new consumer will be wiped out. - Consumer<CaptureResult> consumer = mResultConsumer; - mResultConsumer = null; - consumer.accept(new CaptureResult(image, mRequestRect, contentArea)); + mTileRequestCompleter.set(new CaptureResult(image, mRequestRect, contentArea)); } @Override - public void end(Runnable listener) { - if (DEBUG_SCROLL) { - Log.d(TAG, "end(listener=" + listener + ")"); - } - if (mStarted) { - mShutdownListener = listener; - mReader.close(); + public ListenableFuture<Void> end() { + Log.d(TAG, "end()"); + return CallbackToFutureAdapter.getFuture(completer -> { + if (!mStarted) { + try { + mConnection.asBinder().unlinkToDeath(SessionWrapper.this, 0); + mConnection.close(); + } catch (RemoteException e) { + /* ignore */ + } + mConnection = null; + completer.set(null); + return ""; + } + + mEndCompleter = completer; try { - // listener called from onConnectionClosed callback mConnection.endCapture(); } catch (RemoteException e) { - Log.d(TAG, "Ignored exception from endCapture()", e); - disconnect(); - listener.run(); + completer.setException(e); } - } else { - disconnect(); - listener.run(); - } + return "IScrollCaptureCallbacks#onCaptureEnded"; + }); } - @BinderThread - @Override - public void onCaptureEnded() { - close(); - if (mShutdownListener != null) { - mShutdownListener.run(); - mShutdownListener = null; - } + public void release() { + mReader.close(); } + @BinderThread @Override - public void close() { - if (mConnection != null) { - try { - mConnection.close(); - } catch (RemoteException e) { - /* ignore */ - } - disconnect(); - } - } - - // Misc - - private void disconnect() { - if (mConnection != null) { - mConnection.asBinder().unlinkToDeath(this, 0); + public void onCaptureEnded() { + try { + mConnection.close(); + } catch (RemoteException e) { + /* ignore */ } mConnection = null; + mEndCompleter.set(null); } - /** - * The process hosting the window went away abruptly! - */ - @Override - public void binderDied() { - if (DEBUG_SCROLL) { - Log.d(TAG, "binderDied()"); - } - disconnect(); - } + // Misc @Override public int getPageHeight() { @@ -408,16 +402,5 @@ public class ScrollCaptureClient { public int getMaxTiles() { return mMaxTiles; } - - private void cancelPendingRequest() { - if (mCancellationSignal != null) { - try { - mCancellationSignal.cancel(); - } catch (RemoteException e) { - /* ignore */ - } - mCancellationSignal = null; - } - } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 34094cd81120..4f699041fdb3 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -16,18 +16,27 @@ package com.android.systemui.screenshot; -import android.annotation.UiThread; import android.content.Context; -import android.net.Uri; +import android.graphics.Bitmap; +import android.graphics.HardwareRenderer; +import android.graphics.RecordingCanvas; +import android.graphics.Rect; +import android.graphics.RenderNode; +import android.graphics.drawable.Drawable; import android.provider.Settings; import android.util.Log; +import android.view.IWindowManager; +import android.view.ScrollCaptureResponse; + +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.concurrent.futures.CallbackToFutureAdapter.Completer; import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult; -import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Session; -import java.time.ZonedDateTime; -import java.util.UUID; +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; /** @@ -47,44 +56,136 @@ public class ScrollCaptureController { // or if the desired bitmap size is reached. private boolean mFinishOnBoundary; - private Session mSession; - public static final int MAX_HEIGHT = 12000; private final Context mContext; - - private final Executor mUiExecutor; private final Executor mBgExecutor; - private final ImageExporter mImageExporter; private final ImageTileSet mImageTileSet; + private final ScrollCaptureClient mClient; + + private Completer<LongScreenshot> mCaptureCompleter; + + private ListenableFuture<Session> mSessionFuture; + private Session mSession; + private ListenableFuture<CaptureResult> mTileFuture; + private ListenableFuture<Void> mEndFuture; + + static class LongScreenshot { + private final ImageTileSet mImageTileSet; + private final Session mSession; + + LongScreenshot(Session session, ImageTileSet imageTileSet) { + mSession = session; + mImageTileSet = imageTileSet; + } - private ZonedDateTime mCaptureTime; - private UUID mRequestId; - private ScrollCaptureCallback mCaptureCallback; + /** Returns a bitmap containing the combinded result. */ + public Bitmap toBitmap() { + return mImageTileSet.toBitmap(); + } + + public Bitmap toBitmap(Rect bounds) { + return mImageTileSet.toBitmap(bounds); + } - public ScrollCaptureController(Context context, Executor uiExecutor, Executor bgExecutor, - ImageExporter exporter) { + /** Releases image resources from the screenshot. */ + public void release() { + Log.d(TAG, "LongScreenshot :: release()"); + mImageTileSet.clear(); + mSession.release(); + } + + public int getLeft() { + return mImageTileSet.getLeft(); + } + + public int getTop() { + return mImageTileSet.getTop(); + } + + public int getBottom() { + return mImageTileSet.getBottom(); + } + + public int getWidth() { + return mImageTileSet.getWidth(); + } + + public int getHeight() { + return mImageTileSet.getHeight(); + } + + /** @return the height of the visible area of the scrolling page, in pixels */ + public int getPageHeight() { + return mSession.getPageHeight(); + } + + @Override + public String toString() { + return "LongScreenshot{w=" + mImageTileSet.getWidth() + + ", h=" + mImageTileSet.getHeight() + "}"; + } + + public Drawable getDrawable() { + return mImageTileSet.getDrawable(); + } + } + + ScrollCaptureController(Context context, Executor bgExecutor, IWindowManager wms) { mContext = context; - mUiExecutor = uiExecutor; mBgExecutor = bgExecutor; - mImageExporter = exporter; mImageTileSet = new ImageTileSet(context.getMainThreadHandler()); + mClient = new ScrollCaptureClient(mContext, wms); } /** - * Run scroll capture! + * Run scroll capture. Performs a batch capture, collecting image tiles. * - * @param connection connection to the remote window to be used - * @param callback request callback to report back to the service + * @param response a scroll capture response from a previous request which is + * {@link ScrollCaptureResponse#isConnected() connected}. + * @return a future ImageTile set containing the result */ - public void start(Connection connection, ScrollCaptureCallback callback) { - mCaptureTime = ZonedDateTime.now(); - mRequestId = UUID.randomUUID(); - mCaptureCallback = callback; - - float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(), - SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT); - connection.start(this::startCapture, maxPages); + ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) { + Log.d(TAG, "run: " + response); + return CallbackToFutureAdapter.getFuture(completer -> { + Log.d(TAG, "getFuture(ImageTileSet) "); + mCaptureCompleter = completer; + mBgExecutor.execute(() -> { + Log.d(TAG, "bgExecutor.execute"); + float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(), + SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT); + Log.d(TAG, "client start, maxPages=" + maxPages); + mSessionFuture = mClient.start(response, maxPages); + mSessionFuture.addListener(this::onStartComplete, mContext.getMainExecutor()); + }); + return "<batch scroll capture>"; + }); + } + + private void onStartComplete() { + try { + mSession = mSessionFuture.get(); + Log.d(TAG, "got session " + mSession); + requestNextTile(0); + } catch (InterruptedException | ExecutionException e) { + // Failure to start, propagate to caller + Log.d(TAG, "session start failed!"); + mCaptureCompleter.setException(e); + } + } + + private void requestNextTile(int topPx) { + Log.d(TAG, "requestNextTile: " + topPx); + mTileFuture = mSession.requestTile(topPx); + mTileFuture.addListener(() -> { + try { + Log.d(TAG, "onCaptureResult"); + onCaptureResult(mTileFuture.get()); + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "requestTile failed!", e); + mCaptureCompleter.setException(e); + } + }, mContext.getMainExecutor()); } private void onCaptureResult(CaptureResult result) { @@ -146,49 +247,24 @@ public class ScrollCaptureController { } if (finish) { - Session session = mSession; - mSession = null; Log.d(TAG, "Stop."); - mUiExecutor.execute(() -> afterCaptureComplete(session)); + finishCapture(); return; } int nextTop = (mScrollingUp) ? result.captured.top - mSession.getTileHeight() : result.captured.bottom; - Log.d(TAG, "requestTile: " + nextTop); - mSession.requestTile(nextTop, /* consumer */ this::onCaptureResult); - } - - private void startCapture(Session session) { - mSession = session; - session.requestTile(0, this::onCaptureResult); + requestNextTile(nextTop); } - @UiThread - void afterCaptureComplete(Session session) { - Log.d(TAG, "afterCaptureComplete"); - - if (mImageTileSet.isEmpty()) { - mCaptureCallback.onError(); - } else { - mCaptureCallback.onComplete(mImageTileSet, session.getPageHeight()); - } + private void finishCapture() { + Log.d(TAG, "finishCapture()"); + mEndFuture = mSession.end(); + mEndFuture.addListener(() -> { + Log.d(TAG, "endCapture completed"); + // Provide result to caller and complete the top-level future + // Caller is responsible for releasing this resource (ImageReader/HardwareBuffers) + mCaptureCompleter.set(new LongScreenshot(mSession, mImageTileSet)); + }, mContext.getMainExecutor()); } - - /** - * Callback for image capture completion or error. - */ - public interface ScrollCaptureCallback { - void onComplete(ImageTileSet imageTileSet, int pageHeight); - void onError(); - } - - /** - * Callback for image export completion or error. - */ - public interface ExportCallback { - void onExportComplete(Uri outputUri); - void onError(); - } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 34b29ca9721b..2856ebb4066d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.content.pm.UserInfo; import android.net.Uri; import android.os.Handler; +import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -69,8 +70,10 @@ import com.android.systemui.statusbar.policy.RemoteInputView; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; import java.util.Set; +import java.util.stream.Stream; import dagger.Lazy; @@ -630,24 +633,17 @@ public class NotificationRemoteInputManager implements Dumpable { Notification.Builder b = Notification.Builder .recoverBuilder(mContext, sbn.getNotification().clone()); if (remoteInputText != null || uri != null) { - RemoteInputHistoryItem[] oldHistoryItems = (RemoteInputHistoryItem[]) - sbn.getNotification().extras.getParcelableArray( - Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); - RemoteInputHistoryItem[] newHistoryItems; - - if (oldHistoryItems == null) { - newHistoryItems = new RemoteInputHistoryItem[1]; - } else { - newHistoryItems = new RemoteInputHistoryItem[oldHistoryItems.length + 1]; - System.arraycopy(oldHistoryItems, 0, newHistoryItems, 1, oldHistoryItems.length); - } - RemoteInputHistoryItem newItem; - if (uri != null) { - newItem = new RemoteInputHistoryItem(mimeType, uri, remoteInputText); - } else { - newItem = new RemoteInputHistoryItem(remoteInputText); - } - newHistoryItems[0] = newItem; + RemoteInputHistoryItem newItem = uri != null + ? new RemoteInputHistoryItem(mimeType, uri, remoteInputText) + : new RemoteInputHistoryItem(remoteInputText); + Parcelable[] oldHistoryItems = sbn.getNotification().extras + .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null + ? Stream.concat( + Stream.of(newItem), + Arrays.stream(oldHistoryItems).map(p -> (RemoteInputHistoryItem) p)) + .toArray(RemoteInputHistoryItem[]::new) + : new RemoteInputHistoryItem[] { newItem }; b.setRemoteInputHistory(newHistoryItems); } b.setShowRemoteInputSpinner(showSpinner); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 6ba52156c374..5219ecd1d83c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -16,16 +16,23 @@ package com.android.systemui.statusbar; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.text.format.DateFormat; import android.util.FloatProperty; import android.util.Log; +import android.view.View; import android.view.animation.Interpolator; import androidx.annotation.NonNull; import com.android.internal.annotations.GuardedBy; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.UiEventLogger; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; @@ -81,6 +88,8 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll // Record the HISTORY_SIZE most recent states private int mHistoryIndex = 0; private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE]; + // This is used by InteractionJankMonitor to get callback from HWUI. + private View mView; /** * If any of the system bars is hidden. @@ -236,6 +245,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll @Override public void setDozeAmount(float dozeAmount, boolean animated) { + setAndInstrumentDozeAmount(null, dozeAmount, animated); + } + + @Override + public void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated) { if (mDarkAnimator != null && mDarkAnimator.isRunning()) { if (animated && mDozeAmountTarget == dozeAmount) { return; @@ -244,6 +258,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll } } + // We don't need a new attached view if we already have one. + if ((mView == null || !mView.isAttachedToWindow()) + && (view != null && view.isAttachedToWindow())) { + mView = view; + } mDozeAmountTarget = dozeAmount; if (animated) { startDozeAnimation(); @@ -261,6 +280,22 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget); mDarkAnimator.setInterpolator(Interpolators.LINEAR); mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP); + mDarkAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + cancelInteractionJankMonitor(); + } + + @Override + public void onAnimationEnd(Animator animation) { + endInteractionJankMonitor(); + } + + @Override + public void onAnimationStart(Animator animation) { + beginInteractionJankMonitor(); + } + }); mDarkAnimator.start(); } @@ -277,6 +312,24 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll } } + private void beginInteractionJankMonitor() { + if (mView != null && mView.isAttachedToWindow()) { + InteractionJankMonitor.getInstance().begin(mView, getCujType()); + } + } + + private void endInteractionJankMonitor() { + InteractionJankMonitor.getInstance().end(getCujType()); + } + + private void cancelInteractionJankMonitor() { + InteractionJankMonitor.getInstance().cancel(getCujType()); + } + + private int getCujType() { + return mIsDozing ? CUJ_LOCKSCREEN_TRANSITION_TO_AOD : CUJ_LOCKSCREEN_TRANSITION_FROM_AOD; + } + @Override public boolean goingToFullShade() { return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java index a2e07b289e9d..b6d6ed53b681 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.view.View; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -75,6 +76,15 @@ public interface SysuiStatusBarStateController extends StatusBarStateController */ void setDozeAmount(float dozeAmount, boolean animated); + /** + * Changes the current doze amount, also starts the + * {@link com.android.internal.jank.InteractionJankMonitor InteractionJankMonitor} as possible. + * + * @param view An attached view, which will be used by InteractionJankMonitor. + * @param dozeAmount New doze/dark amount. + * @param animated If change should be animated or not. This will cancel current animations. + */ + void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated); /** * Update the expanded state from {@link StatusBar}'s perspective diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index dbd8580b751e..5f93f4807c73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -41,11 +41,11 @@ import android.app.NotificationChannel; import android.app.NotificationManager.Policy; import android.app.Person; import android.app.RemoteInput; -import android.app.RemoteInputHistoryItem; import android.content.Context; import android.content.pm.ShortcutInfo; import android.net.Uri; import android.os.Bundle; +import android.os.Parcelable; import android.os.SystemClock; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.SnoozeCriterion; @@ -534,8 +534,8 @@ public final class NotificationEntry extends ListEntry { return false; } Bundle extras = mSbn.getNotification().extras; - RemoteInputHistoryItem[] replyTexts = (RemoteInputHistoryItem[]) extras.getParcelableArray( - Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + Parcelable[] replyTexts = + extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); if (!ArrayUtils.isEmpty(replyTexts)) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java index 50bbc38eddf3..3f7b8aff7c51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java @@ -104,7 +104,7 @@ public class ActivatableNotificationViewController @Override public boolean onTouch(View v, MotionEvent ev) { - boolean result; + boolean result = false; if (mBlockNextTouch) { mBlockNextTouch = false; return true; @@ -112,16 +112,20 @@ public class ActivatableNotificationViewController if (ev.getAction() == MotionEvent.ACTION_UP) { mView.setLastActionUpTime(SystemClock.uptimeMillis()); } - if (mNeedsDimming && !mAccessibilityManager.isTouchExplorationEnabled() - && mView.isInteractive()) { + // With a11y, just do nothing. + if (mAccessibilityManager.isTouchExplorationEnabled()) { + return false; + } + if (mNeedsDimming && mView.isInteractive()) { if (mNeedsDimming && !mView.isDimmed()) { // We're actually dimmed, but our content isn't dimmable, // let's ensure we have a ripple return false; } result = mNotificationTapHelper.onTouchEvent(ev, mView.getActualHeight()); - } else { - return false; + } else if (ev.getAction() == MotionEvent.ACTION_UP) { + // If this is a false tap, capture the even so it doesn't result in a click. + return mFalsingManager.isFalseTap(true, 0.1); } return result; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 046fbd5b616b..6cf5c303149c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -77,6 +77,7 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; @@ -215,6 +216,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationGuts mGuts; private NotificationEntry mEntry; private String mAppName; + private FalsingManager mFalsingManager; private FalsingCollector mFalsingCollector; /** @@ -887,6 +889,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + // Other parts of the system may intercept and handle all the falsing. + // Otherwise, if we see motion and follow-on events, try to classify them as a tap. + if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) { + mFalsingManager.isFalseTap(true, 0.3); + } + return super.onInterceptTouchEvent(ev); + } + + @Override public boolean onTouchEvent(MotionEvent event) { if (event.getActionMasked() != MotionEvent.ACTION_DOWN || !isChildInGroup() || isGroupExpanded()) { @@ -1569,6 +1581,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView OnExpandClickListener onExpandClickListener, NotificationMediaManager notificationMediaManager, CoordinateOnClickListener onFeedbackClickListener, + FalsingManager falsingManager, FalsingCollector falsingCollector, StatusBarStateController statusBarStateController, PeopleNotificationIdentifier peopleNotificationIdentifier, @@ -1594,6 +1607,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mOnExpandClickListener = onExpandClickListener; mMediaManager = notificationMediaManager; setOnFeedbackClickListener(onFeedbackClickListener); + mFalsingManager = falsingManager; mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 0d0e97eae519..c9fcdac8e45f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -26,6 +26,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; @@ -78,6 +79,7 @@ public class ExpandableNotificationRowController implements NodeController { private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener; private final NotificationGutsManager mNotificationGutsManager; private final OnUserInteractionCallback mOnUserInteractionCallback; + private final FalsingManager mFalsingManager; private final FalsingCollector mFalsingCollector; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; @@ -104,6 +106,7 @@ public class ExpandableNotificationRowController implements NodeController { NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, OnUserInteractionCallback onUserInteractionCallback, + FalsingManager falsingManager, FalsingCollector falsingCollector, PeopleNotificationIdentifier peopleNotificationIdentifier, Optional<BubblesManager> bubblesManagerOptional) { @@ -125,6 +128,7 @@ public class ExpandableNotificationRowController implements NodeController { mStatusBarStateController = statusBarStateController; mNotificationGutsManager = notificationGutsManager; mOnUserInteractionCallback = onUserInteractionCallback; + mFalsingManager = falsingManager; mOnFeedbackClickListener = mNotificationGutsManager::openGuts; mAllowLongPress = allowLongPress; mFalsingCollector = falsingCollector; @@ -150,6 +154,7 @@ public class ExpandableNotificationRowController implements NodeController { mOnExpandClickListener, mMediaManager, mOnFeedbackClickListener, + mFalsingManager, mFalsingCollector, mStatusBarStateController, mPeopleNotificationIdentifier, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index fdd8f347c248..58b87cd2f492 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -27,8 +27,10 @@ import android.app.Notification; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.AsyncTask; import android.os.CancellationSignal; +import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.util.Log; import android.view.View; @@ -768,10 +770,26 @@ public class NotificationContentInflater implements NotificationRowContentBinder return mReInflateFlags; } + void updateApplicationInfo(StatusBarNotification sbn) { + String packageName = sbn.getPackageName(); + int userId = UserHandle.getUserId(sbn.getUid()); + final ApplicationInfo appInfo; + try { + // This method has an internal cache, so we don't need to add our own caching here. + appInfo = mContext.getPackageManager().getApplicationInfoAsUser(packageName, + PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); + } catch (PackageManager.NameNotFoundException e) { + return; + } + Notification.addFieldsFromContext(appInfo, sbn.getNotification()); + } + @Override protected InflationProgress doInBackground(Void... params) { try { final StatusBarNotification sbn = mEntry.getSbn(); + // Ensure the ApplicationInfo is updated before a builder is recovered. + updateApplicationInfo(sbn); final Notification.Builder recoveredBuilder = Notification.Builder.recoverBuilder(mContext, sbn.getNotification()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 8c21e767c5c9..55a27b2b0052 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -22,8 +22,6 @@ import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; @@ -32,7 +30,6 @@ import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; -import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.NotificationHeaderView; @@ -44,7 +41,6 @@ import android.widget.ImageView; import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -1274,23 +1270,6 @@ public class NotificationContentView extends FrameLayout { } } if (hasRemoteInput) { - int color = entry.getSbn().getNotification().color; - if (color == Notification.COLOR_DEFAULT) { - color = mContext.getColor(R.color.default_remote_input_background); - } - if (mContext.getResources().getBoolean( - com.android.internal.R.bool.config_tintNotificationsWithTheme)) { - Resources.Theme theme = new ContextThemeWrapper(mContext, - com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme(); - TypedArray ta = theme.obtainStyledAttributes( - new int[]{com.android.internal.R.attr.colorAccent}); - color = ta.getColor(0, color); - ta.recycle(); - } - existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color, - mContext.getColor(R.color.remote_input_text_enabled), - mContext.getColor(R.color.remote_input_hint))); - existing.setWrapper(wrapper); existing.setOnVisibilityChangedListener(this::setRemoteInputVisible); @@ -1312,6 +1291,10 @@ public class NotificationContentView extends FrameLayout { } } } + + if (existing != null && entry.getSbn().getNotification().isColorized()) { + existing.overrideBackgroundTintColor(entry.getSbn().getNotification().color); + } return existing; } return null; 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 ca6e53d2ec04..cd712044a60e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -3127,7 +3127,7 @@ public class NotificationPanelViewController extends PanelViewController { } final float dozeAmount = dozing ? 1 : 0; - mStatusBarStateController.setDozeAmount(dozeAmount, animate); + mStatusBarStateController.setAndInstrumentDozeAmount(mView, dozeAmount, animate); } public void setPulsing(boolean pulsing) { @@ -3386,6 +3386,9 @@ public class NotificationPanelViewController extends PanelViewController { return new TouchHandler() { @Override public boolean onInterceptTouchEvent(MotionEvent event) { + if (mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating()) { + return true; + } if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) { return false; } @@ -3413,6 +3416,12 @@ public class NotificationPanelViewController extends PanelViewController { @Override public boolean onTouch(View v, MotionEvent event) { + final boolean showingOrAnimatingAltAuth = + mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating(); + if (showingOrAnimatingAltAuth) { + mStatusBarKeyguardViewManager.resetAlternateAuth(); + } + if (mBlockTouches || (mQsFullyExpanded && mQs != null && mQs.disallowPanelTouches())) { return false; @@ -3464,7 +3473,7 @@ public class NotificationPanelViewController extends PanelViewController { handled = true; } handled |= super.onTouch(v, event); - return !mDozing || mPulsing || handled; + return !mDozing || mPulsing || handled || showingOrAnimatingAltAuth; } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 3ac69378d7d1..ed4f32492c9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -453,7 +453,8 @@ public abstract class PanelViewController { // We need to collapse the panel since we peeked to the small height. mView.postOnAnimation(mPostCollapseRunnable); } - } else if (!mStatusBar.isBouncerShowing()) { + } else if (!mStatusBar.isBouncerShowing() + && !mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating()) { boolean expands = onEmptySpaceClick(mInitialTouchX); onTrackingStopped(expands); } @@ -1394,13 +1395,8 @@ public abstract class PanelViewController { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (mStatusBarKeyguardViewManager.isBouncerShowing() - && mFalsingManager.isFalseTap(true, 0.5)) { - endMotionEvent(event, x, y, true /* forceCancel */); - } else { - addMovement(event); - endMotionEvent(event, x, y, false /* forceCancel */); - } + addMovement(event); + endMotionEvent(event, x, y, false /* forceCancel */); InteractionJankMonitor monitor = InteractionJankMonitor.getInstance(); if (event.getActionMasked() == MotionEvent.ACTION_UP) { monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 9b8b7160c95c..b82863e75fe6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -88,6 +88,17 @@ public enum ScrimState { } }, + AUTH_SCRIMMED { + @Override + public void prepare(ScrimState previousState) { + mFrontTint = Color.BLACK; + + mBehindAlpha = 0f; + mFrontAlpha = .66f; + mBubbleAlpha = 0f; + } + }, + /** * Showing password challenge on the keyguard. */ 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 117921dd860c..5045e95dd53f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -662,6 +662,16 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged"); } }; + + + private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener = + new FalsingManager.FalsingBeliefListener() { + @Override + public void onFalse() { + mStatusBarKeyguardViewManager.reset(true); + } + }; + private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); private HeadsUpAppearanceController mHeadsUpAppearanceController; @@ -1000,6 +1010,8 @@ public class StatusBar extends SystemUI implements DemoMode, mInitController.addPostInitTask( () -> setUpDisableFlags(disabledFlags1, disabledFlags2)); + mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener); + mPluginManager.addPluginListener( new PluginListener<OverlayPlugin>() { private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>(); @@ -4147,7 +4159,7 @@ public class StatusBar extends SystemUI implements DemoMode, } @VisibleForTesting - void updateScrimController() { + public void updateScrimController() { Trace.beginSection("StatusBar#updateScrimController"); // We don't want to end up in KEYGUARD state when we're unlocking with @@ -4163,7 +4175,9 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanelViewController.isLaunchingAffordanceWithPreview(); mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview); - if (mBouncerShowing) { + if (mStatusBarKeyguardViewManager.isShowingAlternativeAuth()) { + mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED); + } else if (mBouncerShowing) { // Bouncer needs the front scrim when it's on top of an activity, // tapping on a notification, editing QS or being dismissed by // FLAG_DISMISS_KEYGUARD_ACTIVITY. 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 01ada0f4c86c..81e24cc25aa6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -38,6 +38,7 @@ import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; import android.widget.FrameLayout; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.util.LatencyTracker; @@ -199,6 +200,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final DockManager mDockManager; private final KeyguardUpdateMonitor mKeyguardUpdateManager; private KeyguardBypassController mBypassController; + @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @@ -271,6 +273,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb registerListeners(); } + public void setAlternateAuthInterceptor(@Nullable AlternateAuthInterceptor authInterceptor) { + mAlternateAuthInterceptor = authInterceptor; + } + private void registerListeners() { mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback); mStatusBarStateController.addCallback(this); @@ -434,11 +440,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mShowing) { // If we were showing the bouncer and then aborting, we need to also clear out any // potential actions unless we actually unlocked. - mAfterKeyguardGoneAction = null; - if (mKeyguardGoneCancelAction != null) { - mKeyguardGoneCancelAction.run(); - mKeyguardGoneCancelAction = null; - } + cancelPostAuthActions(); } mBouncer.hide(destroyView); cancelPendingWakeupAction(); @@ -474,6 +476,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; } + if (mAlternateAuthInterceptor != null + && mAlternateAuthInterceptor.showAlternativeAuthMethod()) { + mStatusBar.updateScrimController(); + mAfterKeyguardGoneAction = r; + mKeyguardGoneCancelAction = cancelAction; + return; + } + if (!afterKeyguardGone) { mBouncer.showWithDismissAction(r, cancelAction); } else { @@ -508,11 +518,21 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } else { showBouncerOrKeyguard(hideBouncerWhenShowing); } + resetAlternateAuth(); mKeyguardUpdateManager.sendKeyguardReset(); updateStates(); } } + /** + * Stop showing any alternate auth methods + */ + public void resetAlternateAuth() { + if (mAlternateAuthInterceptor != null && mAlternateAuthInterceptor.reset()) { + mStatusBar.updateScrimController(); + } + } + @Override public void onStartedWakingUp() { mStatusBar.getNotificationShadeWindowView().getWindowInsetsController() @@ -834,6 +854,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return mBouncer.isFullscreenBouncer(); } + /** + * Clear out any potential actions that were saved to run when the device is unlocked + */ + public void cancelPostAuthActions() { + if (bouncerIsOrWillBeShowing()) { + return; // allow bouncer to trigger saved actions + } + mAfterKeyguardGoneAction = null; + if (mKeyguardGoneCancelAction != null) { + mKeyguardGoneCancelAction.run(); + mKeyguardGoneCancelAction = null; + } + } + private long getNavBarShowDelay() { if (mKeyguardStateController.isKeyguardFadingAway()) { return mKeyguardStateController.getKeyguardFadingAwayDelay(); @@ -1063,6 +1097,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.dump(pw); } + + if (mAlternateAuthInterceptor != null) { + pw.println("AltAuthInterceptor: "); + mAlternateAuthInterceptor.dump(pw); + } } @Override @@ -1079,6 +1118,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return mBouncer; } + public boolean isShowingAlternativeAuth() { + return mAlternateAuthInterceptor != null + && mAlternateAuthInterceptor.isShowingAlternativeAuth(); + } + + public boolean isShowingAlternativeAuthOrAnimating() { + return mAlternateAuthInterceptor != null + && (mAlternateAuthInterceptor.isShowingAlternativeAuth() + || mAlternateAuthInterceptor.isAnimating()); + } + private static class DismissWithActionRequest { final OnDismissAction dismissAction; final Runnable cancelAction; @@ -1093,4 +1143,36 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb this.message = message; } } + + /** + * Delegate used to send show/reset events to an alternate authentication method instead of the + * bouncer. + */ + public interface AlternateAuthInterceptor { + /** + * @return whether alternative auth method was newly shown + */ + boolean showAlternativeAuthMethod(); + + /** + * reset the state to the default (only keyguard showing, no auth methods showing) + * @return whether alternative auth method was newly hidden + */ + boolean reset(); + + /** + * @return true if alternative auth method is showing + */ + boolean isShowingAlternativeAuth(); + + /** + * print information for the alternate auth interceptor registered + */ + void dump(PrintWriter pw); + + /** + * @return true if the new auth method is currently animating in or out. + */ + boolean isAnimating(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java index 59c1138431fb..461587760f56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java @@ -26,6 +26,7 @@ import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardConstants; @@ -143,6 +144,16 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie openQsUserPanel(); }); + mView.setAccessibilityDelegate(new View.AccessibilityDelegate() { + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + info.addAction(new AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.ACTION_CLICK, + mContext.getString( + R.string.accessibility_quick_settings_choose_user_action))); + } + }); + updateView(true /* forceUpdate */); } 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 320b00af0fc5..f72d2ae191d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -31,8 +31,15 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ShortcutManager; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.LayerDrawable; import android.net.Uri; import android.os.Bundle; import android.os.ServiceManager; @@ -70,6 +77,7 @@ import androidx.annotation.NonNull; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -137,12 +145,40 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } + /** + * The remote view needs to adapt to colorized notifications when set + * @param color colorized notification color + */ + public void overrideBackgroundTintColor(int color) { + mEditText.setBackgroundTintColor(color); + final boolean dark = !ContrastColorUtil.isColorLight(color); + int[][] states = new int[][] { + new int[] {android.R.attr.state_enabled}, + new int[] {}, + }; + + final int finalColor = dark + ? Color.WHITE + : Color.BLACK; + + int[] colors = new int[] { + finalColor, + finalColor & 0x4DFFFFFF // %30 opacity + }; + + final ColorStateList tint = new ColorStateList(states, colors); + mSendButton.setImageTintList(tint); + mProgressBar.setProgressTintList(tint); + mProgressBar.setIndeterminateTintList(tint); + mProgressBar.setSecondaryProgressTintList(tint); + mEditText.setForegroundColor(finalColor); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); mProgressBar = findViewById(R.id.remote_input_progress); - mSendButton = findViewById(R.id.remote_input_send); mSendButton.setOnClickListener(this); @@ -362,6 +398,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + mEditText.updateCornerRadius(heightMeasureSpec / 2); + } + /** Populates the text field of the remote input with the given content. */ public void setEditTextContent(@Nullable CharSequence editTextContent) { mEditText.setText(editTextContent); @@ -651,17 +693,47 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private final OnReceiveContentListener mOnReceiveContentListener = this::onReceiveContent; - private final Drawable mBackground; private RemoteInputView mRemoteInputView; + private GradientDrawable mTextBackground; + private ColorDrawable mBackgroundColor; + private LayerDrawable mBackground; boolean mShowImeOnInputConnection; private LightBarController mLightBarController; private InputMethodManager mInputMethodManager; + private int mColor = Notification.COLOR_DEFAULT; UserHandle mUser; + private int mStokeWidth; public RemoteEditText(Context context, AttributeSet attrs) { super(context, attrs); - mBackground = getBackground(); mLightBarController = Dependency.get(LightBarController.class); + mTextBackground = createBackground(context, attrs); + mBackgroundColor = new ColorDrawable(); + mBackground = new LayerDrawable(new Drawable[] {mBackgroundColor, mTextBackground}); + float density = context.getResources().getDisplayMetrics().density; + mStokeWidth = (int) (2 * density); + setDefaultColors(); + } + + private void setDefaultColors() { + Resources.Theme theme = getContext().getTheme(); + TypedArray ta = theme.obtainStyledAttributes( + new int[]{android.R.attr.colorAccent, + com.android.internal.R.attr.colorBackgroundFloating}); + mTextBackground.setStroke(mStokeWidth, + ta.getColor(0, Notification.COLOR_DEFAULT)); + mColor = ta.getColor(1, Notification.COLOR_DEFAULT); + mTextBackground.setColor(mColor); + } + + private GradientDrawable createBackground(Context context, AttributeSet attrs) { + float density = context.getResources().getDisplayMetrics().density; + int padding = (int) (12 * density); + GradientDrawable d = new GradientDrawable(); + d.setShape(GradientDrawable.RECTANGLE); + d.setPadding(padding, padding, padding, padding); + d.setCornerRadius(padding); + return d; } void setSupportedMimeTypes(@Nullable Collection<String> mimeTypes) { @@ -724,6 +796,19 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } + protected void setBackgroundTintColor(int color) { + mBackgroundColor.setColor(color); + mTextBackground.setColor(color); + } + + protected void setForegroundColor(int color) { + mTextBackground.setStroke(mStokeWidth, color); + setTextColor(color); + // %60 + setHintTextColor(color & 0x99FFFFFF); + setTextCursorDrawable(null); + } + @Override public void getFocusedRect(Rect r) { super.getFocusedRect(r); @@ -811,6 +896,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene setSelection(getText().length()); } + void updateCornerRadius(float radius) { + mTextBackground.setCornerRadius(radius); + } + void setInnerFocusable(boolean focusable) { setFocusableInTouchMode(focusable); setFocusable(focusable); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java index e8331a176134..e76b8035cd59 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.policy; import android.app.admin.DeviceAdminInfo; +import android.content.ComponentName; import android.graphics.drawable.Drawable; import com.android.systemui.Dumpable; @@ -33,6 +34,10 @@ public interface SecurityController extends CallbackController<SecurityControlle String getProfileOwnerName(); CharSequence getDeviceOwnerOrganizationName(); CharSequence getWorkProfileOrganizationName(); + /** Device owner component even if not on this user. **/ + ComponentName getDeviceOwnerComponentOnAnyUser(); + /** Device owner type for a device owner. **/ + int getDeviceOwnerType(ComponentName admin); boolean isNetworkLoggingEnabled(); boolean isVpnEnabled(); boolean isVpnRestricted(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 5638503be198..4afb86b1a810 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -15,9 +15,11 @@ */ package com.android.systemui.statusbar.policy; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManager; +import android.app.admin.DevicePolicyManager.DeviceOwnerType; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -225,6 +227,18 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi } @Override + @Nullable + public ComponentName getDeviceOwnerComponentOnAnyUser() { + return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser(); + } + + @Override + @DeviceOwnerType + public int getDeviceOwnerType(@NonNull ComponentName admin) { + return mDevicePolicyManager.getDeviceOwnerType(admin); + } + + @Override public boolean isNetworkLoggingEnabled() { return mDevicePolicyManager.isNetworkLoggingEnabled(null); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index ddfa63a33149..3f4ec8539d38 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -81,7 +81,7 @@ import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.startingsurface.StartingWindowController; -import com.android.wm.shell.transition.ShellTransitions; +import com.android.wm.shell.transition.RemoteTransitions; import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -399,8 +399,8 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static ShellTransitions provideRemoteTransitions(Transitions transitions) { - return transitions.asRemoteTransitions(); + static RemoteTransitions provideRemoteTransitions(Transitions transitions) { + return Transitions.asRemoteTransitions(transitions); } @WMSingleton @@ -509,33 +509,27 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static ShellInit provideShellInit(ShellInitImpl impl) { - return impl.asShellInit(); - } - - @WMSingleton - @Provides - static ShellInitImpl provideShellInitImpl(DisplayImeController displayImeController, + static ShellInit provideShellInit(DisplayImeController displayImeController, DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<AppPairsController> appPairsOptional, + Optional<StartingSurface> startingSurface, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, - StartingWindowController startingWindow, @ShellMainThread ShellExecutor mainExecutor) { - return new ShellInitImpl(displayImeController, + return ShellInitImpl.create(displayImeController, dragAndDropController, shellTaskOrganizer, legacySplitScreenOptional, splitScreenOptional, appPairsOptional, + startingSurface, pipTouchHandlerOptional, fullscreenTaskListener, transitions, - startingWindow, mainExecutor); } @@ -545,13 +539,7 @@ public abstract class WMShellBaseModule { */ @WMSingleton @Provides - static Optional<ShellCommandHandler> provideShellCommandHandler(ShellCommandHandlerImpl impl) { - return Optional.of(impl.asShellCommandHandler()); - } - - @WMSingleton - @Provides - static ShellCommandHandlerImpl provideShellCommandHandlerImpl( + static Optional<ShellCommandHandler> provideShellCommandHandler( ShellTaskOrganizer shellTaskOrganizer, Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, @@ -560,8 +548,8 @@ public abstract class WMShellBaseModule { Optional<HideDisplayCutoutController> hideDisplayCutout, Optional<AppPairsController> appPairsOptional, @ShellMainThread ShellExecutor mainExecutor) { - return new ShellCommandHandlerImpl(shellTaskOrganizer, + return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer, legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional, - hideDisplayCutout, appPairsOptional, mainExecutor); + hideDisplayCutout, appPairsOptional, mainExecutor)); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index 62722778384b..096ce0f9de27 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -41,8 +41,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.classifier.FalsingManagerFake; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -96,9 +94,6 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { private KeyguardMessageArea mKeyguardMessageArea; @Mock private ConfigurationController mConfigurationController; - @Mock - private KeyguardViewController mKeyguardViewController; - private FalsingManager mFalsingManager = new FalsingManagerFake(); private KeyguardSecurityContainerController mKeyguardSecurityContainerController; private KeyguardPasswordViewController mKeyguardPasswordViewController; @@ -124,7 +119,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, mKeyguardSecurityViewFlipperController, - mConfigurationController, mKeyguardViewController, mFalsingManager) + mConfigurationController) .create(mSecurityCallback); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index b9d8d27b8971..e35e9877998c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -195,7 +195,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorPropertiesInternal( 0 /* id */, FaceSensorProperties.STRENGTH_STRONG, 1 /* maxTemplatesAllowed */, - false /* supportsFaceDetection */, true /* supportsSelfIllumination */)); + false /* supportsFaceDetection */, true /* supportsSelfIllumination */, + false /* resetLockoutRequiresChallenge */)); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index d6f4958942dd..be110fcf70ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -44,8 +44,10 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -91,6 +93,10 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private StatusBar mStatusBar; @Mock + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock + private DumpManager mDumpManager; + @Mock private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback; private FakeExecutor mFgExecutor; @@ -129,7 +135,9 @@ public class UdfpsControllerTest extends SysuiTestCase { mWindowManager, mStatusBarStateController, mFgExecutor, - mStatusBar); + mStatusBar, + mStatusBarKeyguardViewManager, + mDumpManager); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index 480b33556b27..65f0f7b87cf4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -26,9 +28,11 @@ import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import org.junit.Before; import org.junit.Test; @@ -51,24 +55,34 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { private StatusBarStateController mStatusBarStateController; @Mock private StatusBar mStatusBar; + @Mock + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock + private DumpManager mDumpManager; private UdfpsKeyguardViewController mController; // Capture listeners so that they can be used to send events @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor; - private StatusBarStateController.StateListener mParentListener; - private StatusBarStateController.StateListener mDozeListener; + private StatusBarStateController.StateListener mParentStatusBarStateListener; + private StatusBarStateController.StateListener mStatusBarStateListener; @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor; private StatusBar.ExpansionChangedListener mExpansionListener; + @Captor private ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor> + mAltAuthInterceptorCaptor; + private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor; + @Before public void setUp() { MockitoAnnotations.initMocks(this); mController = new UdfpsKeyguardViewController( mView, mStatusBarStateController, - mStatusBar); + mStatusBar, + mStatusBarKeyguardViewManager, + mDumpManager); } @Test @@ -86,7 +100,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { @Test public void testViewControllerQueriesSBStateOnAttached() { mController.onViewAttached(); - verify(mStatusBarStateController).getState(); + verify(mStatusBarStateController, times(2)).getState(); verify(mStatusBarStateController).getDozeAmount(); final float dozeAmount = .88f; @@ -106,8 +120,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { captureExpansionListener(); mController.onViewDetached(); - verify(mStatusBarStateController).removeCallback(mParentListener); - verify(mStatusBarStateController).removeCallback(mDozeListener); + verify(mStatusBarStateController).removeCallback(mParentStatusBarStateListener); + verify(mStatusBarStateController).removeCallback(mStatusBarStateListener); verify(mStatusBar).removeExpansionChangedListener(mExpansionListener); } @@ -118,21 +132,88 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { final float linear = .55f; final float eased = .65f; - mDozeListener.onDozeAmountChanged(linear, eased); + mStatusBarStateListener.onDozeAmountChanged(linear, eased); verify(mView).onDozeAmountChanged(linear, eased); } + @Test + public void testShouldNotPauseAuthOnKeyguard() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureExpansionListener(); + + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + assertFalse(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthOnShadeLocked() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureExpansionListener(); + + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); + + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testOverrideShouldPauseAuthOnShadeLocked() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureAltAuthInterceptor(); + + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); + assertTrue(mController.shouldPauseAuth()); + + mAltAuthInterceptor.showAlternativeAuthMethod(); // force show + assertFalse(mController.shouldPauseAuth()); + assertTrue(mAltAuthInterceptor.isShowingAlternativeAuth()); + + mAltAuthInterceptor.reset(); // stop force show + assertTrue(mController.shouldPauseAuth()); + assertFalse(mAltAuthInterceptor.isShowingAlternativeAuth()); + } + + @Test + public void testOnDetachedStateReset() { + // GIVEN view is attached, alt auth is force being shown + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureAltAuthInterceptor(); + + mAltAuthInterceptor.showAlternativeAuthMethod(); // alt auth force show + + // WHEN view is detached + mController.onViewDetached(); + + // THEN alt auth state reports not showing + assertFalse(mAltAuthInterceptor.isShowingAlternativeAuth()); + } + + private void sendStatusBarStateChanged(int statusBarState) { + mStatusBarStateListener.onStateChanged(statusBarState); + mParentStatusBarStateListener.onStateChanged(statusBarState); + } + private void captureStatusBarStateListeners() { verify(mStatusBarStateController, times(2)).addCallback(mStateListenerCaptor.capture()); List<StatusBarStateController.StateListener> stateListeners = mStateListenerCaptor.getAllValues(); - mParentListener = stateListeners.get(0); - mDozeListener = stateListeners.get(1); + mParentStatusBarStateListener = stateListeners.get(0); + mStatusBarStateListener = stateListeners.get(1); } private void captureExpansionListener() { verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture()); mExpansionListener = mExpansionListenerCaptor.getValue(); } + + private void captureAltAuthInterceptor() { + verify(mStatusBarKeyguardViewManager).setAlternateAuthInterceptor( + mAltAuthInterceptorCaptor.capture()); + mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java index b2328504272a..d015f51055b5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java @@ -35,6 +35,7 @@ import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingDataProvider.GestureCompleteListener; import com.android.systemui.dock.DockManagerFake; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -71,7 +72,10 @@ public class BrightLineClassifierTest extends SysuiTestCase { private FalsingClassifier mClassifierB; private final List<MotionEvent> mMotionEventList = new ArrayList<>(); @Mock - private HistoryTracker mHistoryTracker;; + private HistoryTracker mHistoryTracker; + @Mock + private KeyguardStateController mKeyguardStateController; + private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); private final FalsingClassifier.Result mFalsedResult = FalsingClassifier.Result.falsed(1, ""); @@ -88,9 +92,10 @@ public class BrightLineClassifierTest extends SysuiTestCase { mClassifiers.add(mClassifierA); mClassifiers.add(mClassifierB); when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList); + when(mKeyguardStateController.isShowing()).thenReturn(true); mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, mDockManager, mMetricsLogger, mClassifiers, mSingleTapClassfier, mDoubleTapClassifier, - mHistoryTracker, false); + mHistoryTracker, mKeyguardStateController, false); ArgumentCaptor<GestureCompleteListener> gestureCompleteListenerCaptor = @@ -120,7 +125,7 @@ public class BrightLineClassifierTest extends SysuiTestCase { } @Test - public void testIsFalseTouch_ClassffiersPass() { + public void testIsFalseTouch_ClassifiersPass() { assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); } @@ -233,4 +238,18 @@ public class BrightLineClassifierTest extends SysuiTestCase { assertThat(mFakeExecutor.numPending()).isEqualTo(0); } + + @Test + public void testNoFalsingUnlocked() { + when(mKeyguardStateController.isShowing()).thenReturn(false); + + when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mFalsedResult); + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); + + when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult); + assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse(); + + when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalsedResult); + assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java index dc79b8881891..e6aeee7a9184 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java @@ -34,6 +34,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ThresholdSensor; import com.android.systemui.util.time.FakeSystemClock; @@ -62,16 +63,19 @@ public class FalsingCollectorImplTest extends SysuiTestCase { private ProximitySensor mProximitySensor; @Mock private SysuiStatusBarStateController mStatusBarStateController; + @Mock + private KeyguardStateController mKeyguardStateController; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); + when(mKeyguardStateController.isShowing()).thenReturn(true); mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager, mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor, - mStatusBarStateController, new FakeSystemClock()); + mStatusBarStateController, mKeyguardStateController, new FakeSystemClock()); } @Test @@ -159,4 +163,20 @@ public class FalsingCollectorImplTest extends SysuiTestCase { mFalsingCollector.onTouchEvent(up); verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class)); } + + @Test + public void testAvoidUnlocked() { + MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); + MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0); + + when(mKeyguardStateController.isShowing()).thenReturn(false); + + // Nothing passed initially + mFalsingCollector.onTouchEvent(down); + verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class)); + + // Up event would normally flush the up event. + mFalsingCollector.onTouchEvent(up); + verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index 2b76f1c2d76d..22c553b764b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -23,6 +23,7 @@ import static android.inputmethodservice.InputMethodService.IME_VISIBLE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS; import static org.junit.Assert.assertEquals; @@ -31,6 +32,7 @@ import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -44,12 +46,16 @@ import android.content.IntentFilter; import android.hardware.display.DisplayManagerGlobal; import android.os.Handler; import android.os.Looper; +import android.os.SystemClock; import android.os.UserHandle; +import android.provider.DeviceConfig; +import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.Display; import android.view.DisplayInfo; +import android.view.MotionEvent; import android.view.WindowManager; import android.view.WindowMetrics; import android.view.accessibility.AccessibilityManager; @@ -79,6 +85,7 @@ import com.android.systemui.utils.leaks.LeakCheckedTest; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -101,6 +108,7 @@ public class NavigationBarTest extends SysuiTestCase { private OverviewProxyService mOverviewProxyService; private CommandQueue mCommandQueue; private SysUiState mMockSysUiState; + @Mock private Handler mHandler; @Mock private BroadcastDispatcher mBroadcastDispatcher; @@ -124,12 +132,17 @@ public class NavigationBarTest extends SysuiTestCase { mDependency.injectMockDependency(NavigationBarController.class); mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class); TestableLooper.get(this).runWithLooper(() -> { - mHandler = new Handler(); mNavigationBar = createNavBar(mContext); mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal); }); } + @After + public void tearDown() throws Exception { + DeviceConfig.resetToDefaults( + Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI); + } + private void setupSysuiDependency() { Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); @@ -163,6 +176,32 @@ public class NavigationBarTest extends SysuiTestCase { } @Test + public void testHomeLongPressWithCustomDuration() throws Exception { + DeviceConfig.setProperties( + new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI) + .setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100) + .build()); + mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null)); + + mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain( + /*downTime=*/SystemClock.uptimeMillis(), + /*eventTime=*/SystemClock.uptimeMillis(), + /*action=*/MotionEvent.ACTION_DOWN, + 0, 0, 0 + )); + verify(mHandler, times(1)).postDelayed(any(), eq(100L)); + + mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain( + /*downTime=*/SystemClock.uptimeMillis(), + /*eventTime=*/SystemClock.uptimeMillis(), + /*action=*/MotionEvent.ACTION_UP, + 0, 0, 0 + )); + + verify(mHandler, times(1)).removeCallbacks(any()); + } + + @Test public void testRegisteredWithDispatcher() { mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null)); verify(mBroadcastDispatcher).registerReceiverWithHandler( diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index 3fed07472c35..410a809bd394 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -21,15 +21,16 @@ import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY; import static android.app.people.ConversationStatus.ACTIVITY_GAME; import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY; import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE; +import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH; import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; +import static com.android.systemui.people.PeopleSpaceUtils.REQUIRED_WIDTH_FOR_MEDIUM; import static com.android.systemui.people.PeopleSpaceUtils.getPeopleTileFromPersistentStorage; import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -59,6 +60,8 @@ import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; +import android.content.res.Configuration; +import android.content.res.Resources; import android.database.Cursor; import android.graphics.drawable.Icon; import android.net.Uri; @@ -226,6 +229,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { @Mock private NotificationEntryManager mNotificationEntryManager; + private Bundle mOptions; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -233,12 +238,12 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - Bundle options = new Bundle(); - options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE); + mOptions = new Bundle(); + mOptions.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE); when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT))) - .thenReturn(options); + .thenReturn(mOptions); when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT))) .thenReturn(new Bundle()); @@ -252,6 +257,10 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { when(mMockContext.getPackageManager()).thenReturn(mPackageManager); when(mMockContext.getString(R.string.over_timestamp)).thenReturn( mContext.getString(R.string.over_timestamp)); + Configuration configuration = mock(Configuration.class); + Resources resources = mock(Resources.class); + when(mMockContext.getResources()).thenReturn(resources); + when(resources.getConfiguration()).thenReturn(configuration); when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null); when(mNotificationEntryManager.getVisibleNotifications()) .thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3)); @@ -665,7 +674,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { @Test public void testCreateRemoteViewsWithLastInteractionTime() { RemoteViews views = PeopleSpaceUtils.createRemoteViews(mMockContext, - PERSON_TILE_WITHOUT_NOTIFICATION, 0); + PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -676,13 +685,22 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { // No availability. View availability = result.findViewById(R.id.availability); assertEquals(View.GONE, availability.getVisibility()); - // No new story. - View personIcon = result.findViewById(R.id.person_icon_only); - View personIconWithStory = result.findViewById(R.id.person_icon_with_story); + // Shows person icon. + View personIcon = result.findViewById(R.id.person_icon); assertEquals(View.VISIBLE, personIcon.getVisibility()); - assertEquals(View.GONE, personIconWithStory.getVisibility()); // No status. - assertThat((View) result.findViewById(R.id.status)).isNull(); + assertThat((View) result.findViewById(R.id.text_content)).isNull(); + + mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1); + RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext, + PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions); + View smallResult = smallView.apply(mContext, null); + + // Show name over predefined icon. + assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility()); + assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility()); + // Shows person icon. + assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility()); } @Test @@ -694,7 +712,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { PERSON_TILE_WITHOUT_NOTIFICATION.getId(), ACTIVITY_GAME).build())).build(); RemoteViews views = PeopleSpaceUtils.createRemoteViews(mMockContext, - tileWithAvailabilityAndNewStory, 0); + tileWithAvailabilityAndNewStory, 0, mOptions); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -705,13 +723,23 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { // Has availability. View availability = result.findViewById(R.id.availability); assertEquals(View.VISIBLE, availability.getVisibility()); - // Has new story. - View personIcon = result.findViewById(R.id.person_icon_only); - View personIconWithStory = result.findViewById(R.id.person_icon_with_story); - assertEquals(View.GONE, personIcon.getVisibility()); - assertEquals(View.VISIBLE, personIconWithStory.getVisibility()); + // Has person icon. + View personIcon = result.findViewById(R.id.person_icon); + assertEquals(View.VISIBLE, personIcon.getVisibility()); // No status. - assertThat((View) result.findViewById(R.id.status)).isNull(); + assertThat((View) result.findViewById(R.id.text_content)).isNull(); + + mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1); + RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext, + tileWithAvailabilityAndNewStory, 0, mOptions); + View smallResult = smallView.apply(mContext, null); + + // Show name rather than game type. + assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility()); + assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility()); + // Has person icon. + assertEquals(View.VISIBLE, + smallResult.findViewById(R.id.person_icon).getVisibility()); } @Test @@ -723,7 +751,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { PERSON_TILE_WITHOUT_NOTIFICATION.getId(), ACTIVITY_BIRTHDAY).build())).build(); RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext, - tileWithStatusTemplate, 0); + tileWithStatusTemplate, 0, mOptions); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -731,14 +759,25 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { // Has availability. View availability = result.findViewById(R.id.availability); assertEquals(View.VISIBLE, availability.getVisibility()); - // Has new story. - View personIcon = result.findViewById(R.id.person_icon_only); - View personIconWithStory = result.findViewById(R.id.person_icon_with_story); - assertEquals(View.GONE, personIcon.getVisibility()); - assertEquals(View.VISIBLE, personIconWithStory.getVisibility()); + // Has person icon. + View personIcon = result.findViewById(R.id.person_icon); + assertEquals(View.VISIBLE, personIcon.getVisibility()); // Has status text from backup text. - TextView statusContent = (TextView) result.findViewById(R.id.status); + TextView statusContent = (TextView) result.findViewById(R.id.text_content); assertEquals(statusContent.getText(), mContext.getString(R.string.birthday_status)); + + mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1); + RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext, + tileWithStatusTemplate, 0, mOptions); + View smallResult = smallView.apply(mContext, null); + + // Show icon instead of name. + assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility()); + assertEquals(View.VISIBLE, + smallResult.findViewById(R.id.predefined_icon).getVisibility()); + // Has person icon. + assertEquals(View.VISIBLE, + smallResult.findViewById(R.id.person_icon).getVisibility()); } @Test @@ -748,7 +787,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { Arrays.asList(GAME_STATUS, NEW_STORY_WITH_AVAILABILITY)).build(); RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext, - tileWithStatusTemplate, 0); + tileWithStatusTemplate, 0, mOptions); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -756,14 +795,25 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { // Has availability. View availability = result.findViewById(R.id.availability); assertEquals(View.VISIBLE, availability.getVisibility()); - // Has new story. - View personIcon = result.findViewById(R.id.person_icon_only); - View personIconWithStory = result.findViewById(R.id.person_icon_with_story); - assertEquals(View.GONE, personIcon.getVisibility()); - assertEquals(View.VISIBLE, personIconWithStory.getVisibility()); + // Has person icon. + View personIcon = result.findViewById(R.id.person_icon); + assertEquals(View.VISIBLE, personIcon.getVisibility()); // Has status. - TextView statusContent = (TextView) result.findViewById(R.id.status); + TextView statusContent = (TextView) result.findViewById(R.id.text_content); assertEquals(statusContent.getText(), GAME_DESCRIPTION); + + mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1); + RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext, + tileWithStatusTemplate, 0, mOptions); + View smallResult = smallView.apply(mContext, null); + + // Show icon instead of name. + assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility()); + assertEquals(View.VISIBLE, + smallResult.findViewById(R.id.predefined_icon).getVisibility()); + // Has person icon. + assertEquals(View.VISIBLE, + smallResult.findViewById(R.id.person_icon).getVisibility()); } @Test @@ -774,7 +824,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setNotificationContent(MISSED_CALL) .build(); RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext, - tileWithMissedCallNotification, 0); + tileWithMissedCallNotification, 0, mOptions); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -782,16 +832,25 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { // Has availability. View availability = result.findViewById(R.id.availability); assertEquals(View.GONE, availability.getVisibility()); - // Has new story. - View personIcon = result.findViewById(R.id.person_icon_only); - View personIconWithStory = result.findViewById(R.id.person_icon_with_story); + // Has person icon. + View personIcon = result.findViewById(R.id.person_icon); assertEquals(View.VISIBLE, personIcon.getVisibility()); - assertEquals(View.GONE, personIconWithStory.getVisibility()); - // Has status. - TextView statusContent = (TextView) result.findViewById(R.id.status); + // Has missed call notification content. + TextView statusContent = (TextView) result.findViewById(R.id.text_content); assertEquals(statusContent.getText(), MISSED_CALL); - } + mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1); + RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext, + tileWithMissedCallNotification, 0, mOptions); + View smallResult = smallView.apply(mContext, null); + + // Show icon instead of name. + assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility()); + assertEquals(View.VISIBLE, + smallResult.findViewById(R.id.predefined_icon).getVisibility()); + // Has person icon. + assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility()); + } @Test public void testCreateRemoteViewsWithNotificationTemplate() { @@ -800,24 +859,35 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setStatuses(Arrays.asList(GAME_STATUS, NEW_STORY_WITH_AVAILABILITY)).build(); RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext, - tileWithStatusAndNotification, 0); + tileWithStatusAndNotification, 0, mOptions); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); assertEquals(name.getText(), NAME); TextView subtext = (TextView) result.findViewById(R.id.subtext); - assertTrue(subtext.getText().toString().contains("weeks ago")); + assertEquals(View.GONE, subtext.getVisibility()); // Has availability. View availability = result.findViewById(R.id.availability); assertEquals(View.VISIBLE, availability.getVisibility()); - // Has new story. - View personIcon = result.findViewById(R.id.person_icon_only); - View personIconWithStory = result.findViewById(R.id.person_icon_with_story); - assertEquals(View.GONE, personIcon.getVisibility()); - assertEquals(View.VISIBLE, personIconWithStory.getVisibility()); + // Has person icon. + View personIcon = result.findViewById(R.id.person_icon); + assertEquals(View.VISIBLE, personIcon.getVisibility()); // Has notification content. - TextView statusContent = (TextView) result.findViewById(R.id.content); + TextView statusContent = (TextView) result.findViewById(R.id.text_content); assertEquals(statusContent.getText(), NOTIFICATION_CONTENT); + + mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1); + RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext, + tileWithStatusAndNotification, 0, mOptions); + View smallResult = smallView.apply(mContext, null); + + // Show icon instead of name. + assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility()); + assertEquals(View.VISIBLE, + smallResult.findViewById(R.id.predefined_icon).getVisibility()); + // Has person icon. + assertEquals(View.VISIBLE, + smallResult.findViewById(R.id.person_icon).getVisibility()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index aef75beb3d56..096e51b56263 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -626,14 +626,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { mManager.updateWidgets(widgetIdsArray); mClock.advanceTime(MIN_LINGER_DURATION); - // If we had to fetch Tile from persistent storage, we want to make sure we write it to - // options. - verify(mAppWidgetManager, times(1)) - .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), - mBundleArgumentCaptor.capture()); - Bundle bundle = mBundleArgumentCaptor.getValue(); - PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE); - assertThat(tile.getId()).isEqualTo(SHORTCUT_ID); verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), any()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java index 862e3747f602..5422ae831de3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java @@ -14,6 +14,9 @@ package com.android.systemui.qs; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; @@ -24,6 +27,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.ComponentName; +import android.content.DialogInterface; import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.graphics.drawable.VectorDrawable; @@ -74,6 +79,8 @@ public class QSSecurityFooterTest extends SysuiTestCase { private final String VPN_PACKAGE = "TestVPN"; private final String VPN_PACKAGE_2 = "TestVPN 2"; private static final String PARENTAL_CONTROLS_LABEL = "Parental Control App"; + private static final ComponentName DEVICE_OWNER_COMPONENT = + new ComponentName("TestDPC", "Test"); private ViewGroup mRootView; private TextView mFooterText; @@ -101,6 +108,11 @@ public class QSSecurityFooterTest extends SysuiTestCase { mFooterIcon = mRootView.findViewById(R.id.footer_icon); mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon); mFooter.setHostEnvironment(null); + + when(mSecurityController.getDeviceOwnerComponentOnAnyUser()) + .thenReturn(DEVICE_OWNER_COMPONENT); + when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_DEFAULT); } @Test @@ -148,6 +160,27 @@ public class QSSecurityFooterTest extends SysuiTestCase { } @Test + public void testManagedFinancedDeviceWithOwnerName() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + when(mSecurityController.getDeviceOwnerOrganizationName()) + .thenReturn(MANAGING_ORGANIZATION); + when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + + mFooter.refreshState(); + + TestableLooper.get(this).processAllMessages(); + assertEquals(mContext.getString( + R.string.quick_settings_financed_disclosure_named_management, + MANAGING_ORGANIZATION), mFooterText.getText()); + assertEquals(View.VISIBLE, mRootView.getVisibility()); + assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); + // -1 == never set. + assertEquals(-1, mFooterIcon.getLastImageResource()); + } + + @Test public void testManagedDemoMode() { when(mSecurityController.isDeviceManaged()).thenReturn(true); when(mSecurityController.getDeviceOwnerOrganizationName()).thenReturn(null); @@ -383,6 +416,25 @@ public class QSSecurityFooterTest extends SysuiTestCase { } @Test + public void testGetManagementTitleForNonFinancedDevice() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + + assertEquals(mContext.getString(R.string.monitoring_title_device_owned), + mFooter.getManagementTitle(MANAGING_ORGANIZATION)); + } + + @Test + public void testGetManagementTitleForFinancedDevice() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + + assertEquals(mContext.getString(R.string.monitoring_title_financed_device, + MANAGING_ORGANIZATION), + mFooter.getManagementTitle(MANAGING_ORGANIZATION)); + } + + @Test public void testGetManagementMessage_noManagement() { assertEquals(null, mFooter.getManagementMessage( /* isDeviceManaged= */ false, @@ -409,6 +461,21 @@ public class QSSecurityFooterTest extends SysuiTestCase { } @Test + public void testGetManagementMessage_deviceOwner_asFinancedDevice() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + + assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management, + MANAGING_ORGANIZATION, MANAGING_ORGANIZATION), + mFooter.getManagementMessage( + /* isDeviceManaged= */ true, + MANAGING_ORGANIZATION, + /* isProfileOwnerOfOrganizationOwnedDevice= */ false, + /* workProfileOrganizationName= */ null)); + } + + @Test public void testGetManagementMessage_profileOwnerOfOrganizationOwnedDevice() { assertEquals(mContext.getString(R.string.monitoring_description_named_management, MANAGING_ORGANIZATION), @@ -587,6 +654,34 @@ public class QSSecurityFooterTest extends SysuiTestCase { assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText()); } + @Test + public void testCreateDialogViewForFinancedDevice() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + when(mSecurityController.getDeviceOwnerOrganizationName()) + .thenReturn(MANAGING_ORGANIZATION); + when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + + // Initialize AlertDialog which sets the text for the negative button, which is used when + // creating the dialog for a financed device. + mFooter.showDeviceMonitoringDialog(); + // The above statement would display the Quick Settings dialog which requires user input, + // so simulate the press to continue with the unit test (otherwise, it is stuck). + mFooter.onClick(null, DialogInterface.BUTTON_NEGATIVE); + View view = mFooter.createDialogView(); + + TextView managementSubtitle = view.findViewById(R.id.device_management_subtitle); + assertEquals(View.VISIBLE, managementSubtitle.getVisibility()); + assertEquals(mContext.getString(R.string.monitoring_title_financed_device, + MANAGING_ORGANIZATION), managementSubtitle.getText()); + TextView managementMessage = view.findViewById(R.id.device_management_warning); + assertEquals(View.VISIBLE, managementMessage.getVisibility()); + assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management, + MANAGING_ORGANIZATION, MANAGING_ORGANIZATION), managementMessage.getText()); + assertEquals(mContext.getString(R.string.monitoring_button_view_policies), + mFooter.getSettingsButton()); + } + private CharSequence addLink(CharSequence description) { final SpannableStringBuilder message = new SpannableStringBuilder(); message.append(description); diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java new file mode 100644 index 000000000000..25104b8b1d20 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.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.systemui.recents; + +import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableContext; +import android.testing.TestableLooper; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.startingsurface.StartingSurface; +import com.android.wm.shell.transition.RemoteTransitions; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Optional; + +import dagger.Lazy; + +/** + * Unit tests for {@link com.android.systemui.recents.OverviewProxyService} + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class OverviewProxyServiceTest extends SysuiTestCase { + private OverviewProxyService mSpiedOverviewProxyService; + private TestableContext mSpiedContext; + + @Mock private BroadcastDispatcher mMockBroadcastDispatcher; + @Mock private CommandQueue mMockCommandQueue; + @Mock private Lazy<NavigationBarController> mMockNavBarControllerLazy; + @Mock private IPinnedStackAnimationListener mMockPinnedStackAnimationListener; + @Mock private NavigationModeController mMockNavModeController; + @Mock private NotificationShadeWindowController mMockStatusBarWinController; + @Mock private Optional<Pip> mMockPipOptional; + @Mock private Optional<LegacySplitScreen> mMockLegacySplitScreenOptional; + @Mock private Optional<SplitScreen> mMockSplitScreenOptional; + @Mock private Optional<Lazy<StatusBar>> mMockStatusBarOptionalLazy; + @Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional; + @Mock private PackageManager mPackageManager; + @Mock private SysUiState mMockSysUiState; + @Mock private RemoteTransitions mMockTransitions; + @Mock private Optional<StartingSurface> mStartingSurface; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + + mSpiedContext = spy(mContext); + + when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false); + when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager); + + mSpiedOverviewProxyService = spy(new OverviewProxyService(mSpiedContext, mMockCommandQueue, + mMockNavBarControllerLazy, mMockNavModeController, mMockStatusBarWinController, + mMockSysUiState, mMockPipOptional, mMockLegacySplitScreenOptional, + mMockSplitScreenOptional, mMockStatusBarOptionalLazy, mMockOneHandedOptional, + mMockBroadcastDispatcher, mMockTransitions, mStartingSurface)); + } + + @Test + public void testNonPipDevice_shouldNotNotifySwipeToHomeFinished() throws RemoteException { + mSpiedOverviewProxyService.mSysUiProxy.notifySwipeToHomeFinished(); + + verify(mMockPipOptional, never()).ifPresent(any()); + } + + @Test + public void testNonPipDevice_shouldNotSetPinnedStackAnimationListener() throws RemoteException { + mSpiedOverviewProxyService.mSysUiProxy.setPinnedStackAnimationListener( + mMockPinnedStackAnimationListener); + + verify(mMockPipOptional, never()).ifPresent(any()); + } + + @Test + public void testNonPipDevice_shouldNotSetShelfHeight() throws RemoteException { + mSpiedOverviewProxyService.mSysUiProxy.setShelfHeight(true /* visible */, + 100 /* shelfHeight */); + + verify(mMockPipOptional, never()).ifPresent(any()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java index 9e62a6263a43..63f7c9755782 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java @@ -24,6 +24,7 @@ import android.graphics.Paint; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; +import android.os.CancellationSignal; import android.os.ICancellationSignal; import android.os.RemoteException; import android.view.IScrollCaptureCallbacks; @@ -37,19 +38,15 @@ import android.view.Surface; class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { private final int[] mColors = {Color.RED, Color.GREEN, Color.BLUE}; private IScrollCaptureCallbacks mCallbacks; - private Surface mSurface; private Paint mPaint; private int mNextColor; private HwuiContext mHwuiContext; - - FakeScrollCaptureConnection(IScrollCaptureCallbacks cb) { - mCallbacks = cb; - } + private CancellationSignal mCancellationSignal; @Override - public ICancellationSignal startCapture(Surface surface) { - mSurface = surface; - mHwuiContext = new HwuiContext(false, surface); + public ICancellationSignal startCapture(Surface surface, IScrollCaptureCallbacks callbacks) { + mCallbacks = callbacks; + mHwuiContext = new HwuiContext(surface); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); try { @@ -57,7 +54,9 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { } catch (RemoteException e) { e.rethrowAsRuntimeException(); } - return null; + ICancellationSignal signal = CancellationSignal.createTransport(); + mCancellationSignal = CancellationSignal.fromTransport(signal); + return signal; } @Override @@ -72,7 +71,9 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { } catch (RemoteException e) { e.rethrowAsRuntimeException(); } - return null; + ICancellationSignal signal = CancellationSignal.createTransport(); + mCancellationSignal = CancellationSignal.fromTransport(signal); + return signal; } @Override @@ -83,15 +84,15 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { e.rethrowAsRuntimeException(); } finally { mHwuiContext.destroy(); - mSurface = null; mCallbacks = null; } - return null; + ICancellationSignal signal = CancellationSignal.createTransport(); + mCancellationSignal = CancellationSignal.fromTransport(signal); + return signal; } @Override public void close() throws RemoteException { - } // From android.view.Surface, but issues render requests synchronously with waitForPresent(true) @@ -99,21 +100,16 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { private final RenderNode mRenderNode; private final HardwareRenderer mHardwareRenderer; private RecordingCanvas mCanvas; - private final boolean mIsWideColorGamut; - HwuiContext(boolean isWideColorGamut, Surface surface) { + HwuiContext(Surface surface) { mRenderNode = RenderNode.create("HwuiCanvas", null); mRenderNode.setClipToBounds(false); mRenderNode.setForceDarkAllowed(false); - mIsWideColorGamut = isWideColorGamut; mHardwareRenderer = new HardwareRenderer(); mHardwareRenderer.setContentRoot(mRenderNode); mHardwareRenderer.setSurface(surface, true); - mHardwareRenderer.setColorMode( - isWideColorGamut - ? ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT - : ActivityInfo.COLOR_MODE_DEFAULT); + mHardwareRenderer.setColorMode(ActivityInfo.COLOR_MODE_DEFAULT); mHardwareRenderer.setLightSourceAlpha(0.0f, 0.0f); mHardwareRenderer.setLightSourceGeometry(0.0f, 0.0f, 0.0f, 0.0f); } @@ -142,9 +138,5 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { void destroy() { mHardwareRenderer.destroy(); } - - boolean isWideColorGamut() { - return mIsWideColorGamut; - } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java index 802b462ec10e..cf7dc20160ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java @@ -16,15 +16,15 @@ package com.android.systemui.screenshot; -import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; import static java.util.Objects.requireNonNull; @@ -34,7 +34,7 @@ import android.hardware.display.DisplayManager; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.view.Display; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindowManager; import android.view.ScrollCaptureResponse; @@ -43,30 +43,29 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.systemui.SysuiTestCase; import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult; -import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Session; +import com.google.common.util.concurrent.ListenableFuture; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.Spy; import org.mockito.stubbing.Answer; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + @SmallTest @RunWith(AndroidTestingRunner.class) public class ScrollCaptureClientTest extends SysuiTestCase { - private static final float MAX_PAGES = 3f; + private static final float MAX_PAGES = 3.0f; private Context mContext; private IWindowManager mWm; - @Spy private TestableConsumer<Session> mSessionConsumer; - @Spy private TestableConsumer<Connection> mConnectionConsumer; - @Spy private TestableConsumer<CaptureResult> mResultConsumer; - @Mock private Runnable mRunnable; - @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -79,46 +78,50 @@ public class ScrollCaptureClientTest extends SysuiTestCase { } @Test - public void testBasicClientFlow() throws RemoteException { + public void testDetectAndConnect() + throws RemoteException, InterruptedException, ExecutionException, TimeoutException { doAnswer((Answer<Void>) invocation -> { - IScrollCaptureCallbacks cb = invocation.getArgument(3); - cb.onScrollCaptureResponse(new ScrollCaptureResponse.Builder() + IScrollCaptureResponseListener listener = invocation.getArgument(3); + listener.onScrollCaptureResponse(new ScrollCaptureResponse.Builder() .setBoundsInWindow(new Rect(0, 0, 100, 100)) .setWindowBounds(new Rect(0, 0, 100, 100)) - .setConnection(new FakeScrollCaptureConnection(cb)) + .setConnection(new FakeScrollCaptureConnection()) .build()); return null; }).when(mWm).requestScrollCapture(/* displayId */ anyInt(), /* token */ isNull(), - /* taskId */ anyInt(), any(IScrollCaptureCallbacks.class)); + /* taskId */ anyInt(), any(IScrollCaptureResponseListener.class)); // Create client ScrollCaptureClient client = new ScrollCaptureClient(mContext, mWm); - client.request(Display.DEFAULT_DISPLAY, mConnectionConsumer); - verify(mConnectionConsumer, timeout(100)).accept(any(Connection.class)); + // Request scroll capture + ListenableFuture<ScrollCaptureResponse> requestFuture = + client.request(Display.DEFAULT_DISPLAY); + assertNotNull(requestFuture.get(100, TimeUnit.MILLISECONDS)); - Connection conn = mConnectionConsumer.getValue(); + ScrollCaptureResponse response = requestFuture.get(); + assertTrue(response.isConnected()); - conn.start(mSessionConsumer, MAX_PAGES); - verify(mSessionConsumer, timeout(100)).accept(any(Session.class)); + // Start a session + ListenableFuture<Session> startFuture = client.start(response, MAX_PAGES); + assertNotNull(startFuture.get(100, TimeUnit.MILLISECONDS)); - Session session = mSessionConsumer.getValue(); + Session session = startFuture.get(); Rect request = new Rect(0, 0, session.getPageWidth(), session.getTileHeight()); - session.requestTile(0, mResultConsumer); - verify(mResultConsumer, timeout(100)).accept(any(CaptureResult.class)); + // Request a tile + ListenableFuture<CaptureResult> tileFuture = session.requestTile(0); + assertNotNull(tileFuture.get(100, TimeUnit.MILLISECONDS)); - CaptureResult result = mResultConsumer.getValue(); - assertThat(result.requested).isEqualTo(request); - assertThat(result.captured).isEqualTo(result.requested); - assertThat(result.image).isNotNull(); + CaptureResult result = tileFuture.get(); + assertEquals(request, result.requested); + assertEquals(result.requested, result.captured); + assertNotNull(result.image); - session.end(mRunnable); - verify(mRunnable, timeout(100)).run(); - - // TODO verify image - // TODO test threading - // TODO test failures + // End the session + ListenableFuture<Void> endFuture = session.end(); + CountDownLatch latch = new CountDownLatch(1); + endFuture.addListener(latch::countDown, Runnable::run); + assertTrue(latch.await(100, TimeUnit.MILLISECONDS)); } - } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java index 6564d588f4ea..06b39abd1d0e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java @@ -16,15 +16,16 @@ package com.android.systemui.screenshot; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.content.Intent; -import android.graphics.Rect; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.util.Log; import android.view.Display; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindowManager; import android.view.ScrollCaptureResponse; import android.view.WindowManagerGlobal; @@ -45,8 +46,9 @@ import java.util.concurrent.TimeUnit; */ @RunWith(AndroidTestingRunner.class) @SmallTest -public class ScrollCaptureTest extends SysuiTestCase { - private static final String TAG = "ScrollCaptureTest"; +public class ScrollCaptureFrameworkSmokeTest extends SysuiTestCase { + private static final String TAG = "ScrollCaptureFrameworkSmokeTest"; + private volatile ScrollCaptureResponse mResponse; /** * Verifies that a request traverses from SystemUI, to WindowManager and to the app process and @@ -64,34 +66,23 @@ public class ScrollCaptureTest extends SysuiTestCase { final CountDownLatch latch = new CountDownLatch(1); try { wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1, - new IScrollCaptureCallbacks.Stub() { + new IScrollCaptureResponseListener.Stub() { @Override - public void onScrollCaptureResponse(ScrollCaptureResponse response) + public void onScrollCaptureResponse( + ScrollCaptureResponse response) throws RemoteException { - Log.d(TAG, "onScrollCaptureResponse: " + response); + mResponse = response; latch.countDown(); } - - @Override - public void onCaptureStarted() { - } - - @Override - public void onImageRequestCompleted(int i, Rect rect) - throws RemoteException { - - } - - @Override - public void onCaptureEnded() throws RemoteException { - - } - }); } catch (RemoteException e) { Log.e(TAG, "request failed", e); fail("caught remote exception " + e); } latch.await(1000, TimeUnit.MILLISECONDS); + + assertNotNull(mResponse); + assertTrue(mResponse.isConnected()); + assertTrue(mResponse.getWindowTitle().contains(ScrollViewActivity.class.getSimpleName())); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java deleted file mode 100644 index a554e5f583e7..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java +++ /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. - */ - -package com.android.systemui.screenshot; - -import java.util.function.Consumer; - -/** Accepts and retains the most recent value for verification */ -class TestableConsumer<T> implements Consumer<T> { - T mValue; - - @Override - public void accept(T t) { - mValue = t; - } - - public T getValue() { - return mValue; - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 97313bafb8a8..950b95f41542 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -46,6 +46,7 @@ import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingCollectorFake; +import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -260,6 +261,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mGutsManager, true, null, + new FalsingManagerFake(), new FalsingCollectorFake(), mPeopleNotificationIdentifier, Optional.of(mock(BubblesManager.class)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index f3813fcaf6fc..b4a3393ab5db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -45,6 +45,7 @@ import android.widget.RemoteViews; import com.android.systemui.TestableDependency; import com.android.systemui.classifier.FalsingCollectorFake; +import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -429,6 +430,7 @@ public class NotificationTestHelper { mock(OnExpandClickListener.class), mock(NotificationMediaManager.class), mock(ExpandableNotificationRow.CoordinateOnClickListener.class), + new FalsingManagerFake(), new FalsingCollectorFake(), mStatusBarStateController, mPeopleNotificationIdentifier, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index b1f1b5e78b5c..116e1b98debd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -917,11 +917,11 @@ public class ScrimControllerTest extends SysuiTestCase { HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList( ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER, ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED, - ScrimState.BUBBLE_EXPANDED, ScrimState.SHADE_LOCKED)); + ScrimState.BUBBLE_EXPANDED, ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED)); for (ScrimState state : ScrimState.values()) { if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) { - Assert.fail("Scrim state not whitelisted nor blacklisted as low power mode"); + Assert.fail("Scrim state isn't categorized as a low power or regular state."); } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java index 44184ee8eeed..ed87a4040022 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -62,6 +64,9 @@ import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class SecurityControllerTest extends SysuiTestCase { + private static final ComponentName DEVICE_OWNER_COMPONENT = + new ComponentName("com.android.foo", "bar"); + private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class); private final IKeyChainService.Stub mKeyChainService = mock(IKeyChainService.Stub.class); private final UserManager mUserManager = mock(UserManager.class); @@ -127,6 +132,22 @@ public class SecurityControllerTest extends SysuiTestCase { } @Test + public void testGetDeviceOwnerComponentOnAnyUser() { + when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) + .thenReturn(DEVICE_OWNER_COMPONENT); + assertEquals(mSecurityController.getDeviceOwnerComponentOnAnyUser(), + DEVICE_OWNER_COMPONENT); + } + + @Test + public void testGetDeviceOwnerType() { + when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + assertEquals(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT), + DEVICE_OWNER_TYPE_FINANCED); + } + + @Test public void testWorkAccount() throws Exception { assertFalse(mSecurityController.hasCACertInCurrentUser()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java index c0722a459929..3640bcd64c14 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java @@ -15,6 +15,7 @@ package com.android.systemui.utils.leaks; import android.app.admin.DeviceAdminInfo; +import android.content.ComponentName; import android.graphics.drawable.Drawable; import android.testing.LeakCheck; @@ -68,6 +69,16 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa } @Override + public ComponentName getDeviceOwnerComponentOnAnyUser() { + return null; + } + + @Override + public int getDeviceOwnerType(ComponentName admin) { + return 0; + } + + @Override public boolean isNetworkLoggingEnabled() { return false; } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index c7f0efa18568..3d07da5fbb30 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -118,6 +118,7 @@ public class FullScreenMagnificationController { private static final int INVALID_ID = -1; private int mIdOfLastServiceToMagnify = INVALID_ID; + private boolean mMagnificationActivated = false; DisplayMagnification(int displayId) { mDisplayId = displayId; @@ -322,6 +323,13 @@ public class FullScreenMagnificationController { mSpecAnimationBridge, spec, animationCallback); mControllerCtx.getHandler().sendMessage(m); } + + final boolean lastMagnificationActivated = mMagnificationActivated; + mMagnificationActivated = spec.scale > 1.0f; + if (mMagnificationActivated != lastMagnificationActivated) { + mMagnificationRequestObserver.onFullScreenMagnificationActivationState( + mMagnificationActivated); + } } /** @@ -1506,5 +1514,14 @@ public class FullScreenMagnificationController { * @param serviceId the ID of the service requesting the change */ void onRequestMagnificationSpec(int displayId, int serviceId); + + /** + * Called when the state of the magnification activation is changed. + * It is for the logging data of the magnification activation state. + * + * @param activated {@code true} if the magnification is activated, otherwise {@code false}. + */ + @GuardedBy("mLock") + void onFullScreenMagnificationActivationState(boolean activated); } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 2a65b6423158..17a7d393f369 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -16,6 +16,7 @@ package com.android.server.accessibility.magnification; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; @@ -25,11 +26,13 @@ import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; +import android.os.SystemClock; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; import android.view.accessibility.MagnificationAnimationCallback; +import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.server.accessibility.AccessibilityManagerService; @@ -51,7 +54,8 @@ import com.android.server.accessibility.AccessibilityManagerService; * </ol> */ public class MagnificationController implements WindowMagnificationManager.Callback, - MagnificationGestureHandler.Callback { + MagnificationGestureHandler.Callback, + FullScreenMagnificationController.MagnificationRequestObserver { private static final boolean DEBUG = false; private static final String TAG = "MagnificationController"; @@ -66,6 +70,9 @@ public class MagnificationController implements WindowMagnificationManager.Callb private WindowMagnificationManager mWindowMagnificationMgr; private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + private long mWindowModeEnabledTime = 0; + private long mFullScreenModeEnabledTime = 0; + /** * A callback to inform the magnification transition result. */ @@ -187,7 +194,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb setDisableMagnificationCallbackLocked(displayId, animationEndCallback); } - void onRequestMagnificationSpec(int displayId, int serviceId) { + @Override + public void onRequestMagnificationSpec(int displayId, int serviceId) { synchronized (mLock) { if (serviceId == AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID) { return; @@ -200,6 +208,39 @@ public class MagnificationController implements WindowMagnificationManager.Callb } } + // TODO : supporting multi-display (b/182227245). + @Override + public void onWindowMagnificationActivationState(boolean activated) { + if (activated) { + mWindowModeEnabledTime = SystemClock.uptimeMillis(); + } else { + logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + SystemClock.uptimeMillis() - mWindowModeEnabledTime); + } + } + + @Override + public void onFullScreenMagnificationActivationState(boolean activated) { + if (activated) { + mFullScreenModeEnabledTime = SystemClock.uptimeMillis(); + } else { + logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, + SystemClock.uptimeMillis() - mFullScreenModeEnabledTime); + } + } + + /** + * Wrapper method of logging the magnification activated mode and its duration of the usage + * when the magnification is disabled. + * + * @param mode The activated magnification mode. + * @param duration The duration in milliseconds during the magnification is activated. + */ + @VisibleForTesting + public void logMagnificationUsageState(int mode, long duration) { + AccessibilityStatsLogUtils.logMagnificationUsageState(mode, duration); + } + /** * Updates the active user ID of {@link FullScreenMagnificationController} and {@link * WindowMagnificationManager}. @@ -260,7 +301,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb synchronized (mLock) { if (mFullScreenMagnificationController == null) { mFullScreenMagnificationController = new FullScreenMagnificationController(mContext, - mAms, mLock, this::onRequestMagnificationSpec); + mAms, mLock, this); mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked()); } } @@ -340,7 +381,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb mTransitionCallBack = transitionCallBack; mDisplayId = displayId; mTargetMode = targetMode; - mCurrentMode = mTargetMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; + mCurrentMode = mTargetMode ^ ACCESSIBILITY_MAGNIFICATION_MODE_ALL; mCurrentScale = scale; mCurrentCenter.set(currentCenter); } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java index 40668d8df568..ded601e70ff1 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -93,6 +93,13 @@ public class WindowMagnificationManager implements * @param scale the target scale, or {@link Float#NaN} to leave unchanged */ void onPerformScaleAction(int displayId, float scale); + + /** + * Called when the state of the magnification activation is changed. + * + * @param activated {@code true} if the magnification is activated, otherwise {@code false}. + */ + void onWindowMagnificationActivationState(boolean activated); } private final Callback mCallback; @@ -264,6 +271,7 @@ public class WindowMagnificationManager implements */ void enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback) { + final boolean enabled; synchronized (mLock) { if (mConnectionWrapper == null) { return; @@ -272,9 +280,13 @@ public class WindowMagnificationManager implements if (magnifier == null) { magnifier = createWindowMagnifier(displayId); } - magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, + enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, animationCallback); } + + if (enabled) { + mCallback.onWindowMagnificationActivationState(true); + } } /** @@ -296,16 +308,21 @@ public class WindowMagnificationManager implements */ void disableWindowMagnification(int displayId, boolean clear, MagnificationAnimationCallback animationCallback) { + final boolean disabled; synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null || mConnectionWrapper == null) { return; } - magnifier.disableWindowMagnificationInternal(animationCallback); + disabled = magnifier.disableWindowMagnificationInternal(animationCallback); if (clear) { mWindowMagnifiers.delete(displayId); } } + + if (disabled) { + mCallback.onWindowMagnificationActivationState(false); + } } /** @@ -560,26 +577,35 @@ public class WindowMagnificationManager implements } @GuardedBy("mLock") - void enableWindowMagnificationInternal(float scale, float centerX, float centerY, + boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback) { if (mEnabled) { - return; + return false; } final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale, centerX, centerY, animationCallback)) { mScale = normScale; mEnabled = true; + + return true; } + return false; } @GuardedBy("mLock") - void disableWindowMagnificationInternal( + boolean disableWindowMagnificationInternal( @Nullable MagnificationAnimationCallback animationResultCallback) { - if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal( + if (!mEnabled) { + return false; + } + if (mWindowMagnificationManager.disableWindowMagnificationInternal( mDisplayId, animationResultCallback)) { mEnabled = false; + + return true; } + return false; } @GuardedBy("mLock") diff --git a/services/api/current.txt b/services/api/current.txt index 7e8f7a20bd64..a3e671520753 100644 --- a/services/api/current.txt +++ b/services/api/current.txt @@ -88,6 +88,14 @@ package com.android.server { } +package com.android.server.am { + + public interface ActivityManagerLocal { + method public boolean canStartForegroundService(int, int, @NonNull String); + } + +} + package com.android.server.role { public interface RoleServicePlatformHelper { diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt index 3c72d38927bc..f01c1824116c 100644 --- a/services/api/non-updatable-current.txt +++ b/services/api/non-updatable-current.txt @@ -35,6 +35,14 @@ package com.android.server { } +package com.android.server.am { + + public interface ActivityManagerLocal { + method public boolean canStartForegroundService(int, int, @NonNull String); + } + +} + package com.android.server.role { public interface RoleServicePlatformHelper { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 688b33e0f685..f666490d13aa 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -55,6 +55,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.uidRulesToString; +import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST; import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired; import static android.os.Process.INVALID_UID; import static android.os.Process.VPN_UID; @@ -117,6 +118,7 @@ import android.net.NetworkMonitorManager; import android.net.NetworkPolicyManager; import android.net.NetworkProvider; import android.net.NetworkRequest; +import android.net.NetworkScore; import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkStackClient; @@ -1273,12 +1275,18 @@ public class ConnectivityService extends IConnectivityManager.Stub mDnsManager = new DnsManager(mContext, mDnsResolver); registerPrivateDnsSettingsCallbacks(); + // This NAI is a sentinel used to offer no service to apps that are on a multi-layer + // request that doesn't allow fallback to the default network. It should never be visible + // to apps. As such, it's not in the list of NAIs and doesn't need many of the normal + // arguments like the handler or the DnsResolver. + // TODO : remove this ; it is probably better handled with a sentinel request. mNoServiceNetwork = new NetworkAgentInfo(null, new Network(NO_SERVICE_NET_ID), new NetworkInfo(TYPE_NONE, 0, "", ""), - new LinkProperties(), new NetworkCapabilities(), 0, mContext, - null, new NetworkAgentConfig(), this, null, - null, 0, INVALID_UID, mQosCallbackTracker, mDeps); + new LinkProperties(), new NetworkCapabilities(), + new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null, + new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker, + mDeps); } private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) { @@ -2949,7 +2957,7 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: { - updateNetworkScore(nai, msg.arg1); + updateNetworkScore(nai, (NetworkScore) arg.second); break; } case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: { @@ -3656,6 +3664,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRequestInfoLogs.log("REGISTER " + nri); for (final NetworkRequest req : nri.mRequests) { mNetworkRequests.put(req, nri); + // TODO: Consider update signal strength for other types. if (req.isListen()) { for (final NetworkAgentInfo network : mNetworkAgentInfos) { if (req.networkCapabilities.hasSignalStrength() @@ -3748,18 +3757,19 @@ public class ConnectivityService extends IConnectivityManager.Stub // listen requests won't keep up a network satisfying it. If this is not a multilayer // request, return immediately. For multilayer requests, check to see if any of the // multilayer requests may have a potential satisfier. - if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) { + if (!nri.isMultilayerRequest() && (nri.mRequests.get(0).isListen() + || nri.mRequests.get(0).isListenForBest())) { return false; } for (final NetworkRequest req : nri.mRequests) { // This multilayer listen request is satisfied therefore no further requests need to be // evaluated deeming this network not a potential satisfier. - if (req.isListen() && nri.getActiveRequest() == req) { + if ((req.isListen() || req.isListenForBest()) && nri.getActiveRequest() == req) { return false; } // As non-multilayer listen requests have already returned, the below would only happen // for a multilayer request therefore continue to the next request if available. - if (req.isListen()) { + if (req.isListen() || req.isListenForBest()) { continue; } // If this Network is already the highest scoring Network for a request, or if @@ -3799,8 +3809,7 @@ public class ConnectivityService extends IConnectivityManager.Stub ? mNetworkRequests.get(request) : getNriForAppRequest(request); if (nri != null) { - if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid - && nri.mUid != callingUid) { + if (Process.SYSTEM_UID != callingUid && nri.mUid != callingUid) { log(String.format("UID %d attempted to %s for unowned request %s", callingUid, requestedOperation, nri)); return null; @@ -5543,8 +5552,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // request if the app changes network state. http://b/29964605 enforceMeteredApnPolicy(networkCapabilities); break; - case TRACK_BEST: - throw new UnsupportedOperationException("Not implemented yet"); + case LISTEN_FOR_BEST: + enforceAccessPermission(); + networkCapabilities = new NetworkCapabilities(networkCapabilities); + break; default: throw new IllegalArgumentException("Unsupported request type " + reqType); } @@ -5552,11 +5563,17 @@ public class ConnectivityService extends IConnectivityManager.Stub ensureSufficientPermissionsForRequest(networkCapabilities, Binder.getCallingPid(), callingUid, callingPackageName); - // Set the UID range for this request to the single UID of the requester, or to an empty - // set of UIDs if the caller has the appropriate permission and UIDs have not been set. + // Enforce FOREGROUND if the caller does not have permission to use background network. + if (reqType == LISTEN_FOR_BEST) { + restrictBackgroundRequestForCaller(networkCapabilities); + } + + // Set the UID range for this request to the single UID of the requester, unless the + // requester has the permission to specify other UIDs. // This will overwrite any allowed UIDs in the requested capabilities. Though there // are no visible methods to set the UIDs, an app could use reflection to try and get // networks for other apps so it's essential that the UIDs are overwritten. + // Also set the requester UID and package name in the request. restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities, callingUid, callingPackageName); @@ -6083,20 +6100,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return nai == getDefaultNetwork(); } - // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent - // changes that would conflict throughout the automerger graph. Having this method temporarily - // helps with the process of going through with all these dependent changes across the entire - // tree. - /** - * Register a new agent. {@see #registerNetworkAgent} below. - */ - public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo, - LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - int currentScore, NetworkAgentConfig networkAgentConfig) { - return registerNetworkAgent(na, networkInfo, linkProperties, networkCapabilities, - currentScore, networkAgentConfig, NetworkProvider.ID_NONE); - } - /** * Register a new agent with ConnectivityService to handle a network. * @@ -6107,7 +6110,7 @@ public class ConnectivityService extends IConnectivityManager.Stub * later : see {@link #updateLinkProperties}. * @param networkCapabilities the initial capabilites of this network. They can be updated * later : see {@link #updateCapabilities}. - * @param currentScore the initial score of the network. See + * @param initialScore the initial score of the network. See * {@link NetworkAgentInfo#getCurrentScore}. * @param networkAgentConfig metadata about the network. This is never updated. * @param providerId the ID of the provider owning this NetworkAgent. @@ -6115,10 +6118,12 @@ public class ConnectivityService extends IConnectivityManager.Stub */ public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) { + @NonNull NetworkScore initialScore, NetworkAgentConfig networkAgentConfig, + int providerId) { Objects.requireNonNull(networkInfo, "networkInfo must not be null"); Objects.requireNonNull(linkProperties, "linkProperties must not be null"); Objects.requireNonNull(networkCapabilities, "networkCapabilities must not be null"); + Objects.requireNonNull(initialScore, "initialScore must not be null"); Objects.requireNonNull(networkAgentConfig, "networkAgentConfig must not be null"); if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS); @@ -6130,7 +6135,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final long token = Binder.clearCallingIdentity(); try { return registerNetworkAgentInternal(na, networkInfo, linkProperties, - networkCapabilities, currentScore, networkAgentConfig, providerId, uid); + networkCapabilities, initialScore, networkAgentConfig, providerId, uid); } finally { Binder.restoreCallingIdentity(token); } @@ -6138,7 +6143,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private Network registerNetworkAgentInternal(INetworkAgent na, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - int currentScore, NetworkAgentConfig networkAgentConfig, int providerId, int uid) { + NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId, + int uid) { if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never @@ -7862,7 +7868,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final int score) { + private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final NetworkScore score) { if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + score); nai.setScore(score); rematchAllNetworksAndRequests(); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 5adbdff150ea..7276c78b398c 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -60,6 +60,7 @@ import android.telephony.CellSignalStrengthNr; import android.telephony.CellSignalStrengthTdscdma; import android.telephony.CellSignalStrengthWcdma; import android.telephony.DisconnectCause; +import android.telephony.LinkCapacityEstimate; import android.telephony.LocationAccessPolicy; import android.telephony.PhoneCapability; import android.telephony.PhoneStateListener; @@ -321,6 +322,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private int[] mAllowedNetworkTypeReason; private long[] mAllowedNetworkTypeValue; + private List<List<LinkCapacityEstimate>> mLinkCapacityEstimateLists; + /** * 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. @@ -351,6 +354,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( TelephonyCallback.EVENT_DATA_ENABLED_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED); } private boolean isLocationPermissionRequired(Set<Integer> events) { @@ -539,6 +544,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { cutListToSize(mPreciseDataConnectionStates, mNumPhones); cutListToSize(mBarringInfo, mNumPhones); cutListToSize(mPhysicalChannelConfigs, mNumPhones); + cutListToSize(mLinkCapacityEstimateLists, mNumPhones); return; } @@ -577,6 +583,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); mAllowedNetworkTypeReason[i] = -1; mAllowedNetworkTypeValue[i] = -1; + mLinkCapacityEstimateLists.add(i, new ArrayList<>()); } } @@ -640,6 +647,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mAllowedNetworkTypeValue = new long[numPhones]; mIsDataEnabled = new boolean[numPhones]; mDataEnabledReason = new int[numPhones]; + mLinkCapacityEstimateLists = new ArrayList<>(); for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; @@ -675,6 +683,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); mAllowedNetworkTypeReason[i] = -1; mAllowedNetworkTypeValue[i] = -1; + mLinkCapacityEstimateLists.add(i, new ArrayList<>()); } mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -1182,6 +1191,17 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if (events.contains( + TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)) { + try { + if (mLinkCapacityEstimateLists.get(phoneId) != null) { + r.callback.onLinkCapacityEstimateChanged(mLinkCapacityEstimateLists + .get(phoneId)); + } + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } @@ -2492,6 +2512,42 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + /** + * Notify that the link capacity estimate has changed. + * @param phoneId the phone id. + * @param subId the subscription id. + * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate} + */ + public void notifyLinkCapacityEstimateChanged(int phoneId, int subId, + List<LinkCapacityEstimate> linkCapacityEstimateList) { + if (!checkNotifyPermission("notifyLinkCapacityEstimateChanged()")) { + return; + } + + if (VDBG) { + log("notifyLinkCapacityEstimateChanged: linkCapacityEstimateList =" + + linkCapacityEstimateList); + } + + synchronized (mRecords) { + if (validatePhoneId(phoneId)) { + mLinkCapacityEstimateLists.set(phoneId, linkCapacityEstimateList); + for (Record r : mRecords) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED) + && idMatch(r.subId, subId, phoneId)) { + try { + r.callback.onLinkCapacityEstimateChanged(linkCapacityEstimateList); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + } + handleRemoveListLocked(); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); @@ -2538,6 +2594,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mDataEnabledReason=" + mDataEnabledReason); pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]); pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]); + pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i)); pw.decreaseIndent(); } pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 73755231c3be..c360190d58a3 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -1076,7 +1076,7 @@ public class AccountManagerService } catch (RuntimeException e) { // The account manager only throws security exceptions, so let's // log all others. - if (!(e instanceof SecurityException)) { + if (!(e instanceof SecurityException || e instanceof IllegalArgumentException)) { Slog.wtf(TAG, "Account Manager Crash", e); } throw e; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 673749c08318..3c445ae4b667 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -24,6 +24,7 @@ import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; +import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; import static android.os.PowerWhitelistManager.REASON_ACTIVITY_STARTER; import static android.os.PowerWhitelistManager.REASON_ALLOWLISTED_PACKAGE; import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; @@ -5409,16 +5410,28 @@ public final class ActiveServices { } } + boolean canStartForegroundServiceLocked(int callingPid, int callingUid, String callingPackage) { + if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { + return true; + } + final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( + callingPackage, callingPid, callingUid, null /* serviceRecord */, + false /* allowBackgroundActivityStarts */); + final @ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundLocked( + allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */); + return allowStartFgs != REASON_DENIED; + } + /** * Should allow while-in-use permissions in FGS or not. * A typical BG started FGS is not allowed to have while-in-use permissions. * @param callingPackage caller app's package name. * @param callingUid caller app's uid. - * @param r the service to start. + * @param targetService the service to start. * @return {@link ReasonCode} */ private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage, - int callingPid, int callingUid, ServiceRecord r, + int callingPid, int callingUid, @Nullable ServiceRecord targetService, boolean allowBackgroundActivityStarts) { int ret = REASON_DENIED; @@ -5480,8 +5493,8 @@ public final class ActiveServices { } if (ret == REASON_DENIED) { - if (r.app != null) { - ActiveInstrumentation instr = r.app.getActiveInstrumentation(); + if (targetService != null && targetService.app != null) { + ActiveInstrumentation instr = targetService.app.getActiveInstrumentation(); if (instr != null && instr.mHasBackgroundActivityStartsPermission) { ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; } @@ -5527,16 +5540,44 @@ public final class ActiveServices { private @ReasonCode int shouldAllowFgsStartForegroundLocked( @ReasonCode int allowWhileInUse, String callingPackage, int callingPid, int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) { - int ret = allowWhileInUse; FgsStartTempAllowList.TempFgsAllowListEntry tempAllowListReason = r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid); + int ret = shouldAllowFgsStartForegroundLocked(allowWhileInUse, callingPid, callingUid, + callingPackage, r); - final StringBuilder sb = new StringBuilder(64); final int uidState = mAm.getUidStateLocked(callingUid); + final String debugInfo = + "[callingPackage: " + callingPackage + + "; callingUid: " + callingUid + + "; uidState: " + ProcessList.makeProcStateString(uidState) + + "; intent: " + intent + + "; code:" + reasonCodeToString(ret) + + "; tempAllowListReason:<" + + (tempAllowListReason == null ? null : + (tempAllowListReason.mReason + + ",reasonCode:" + + reasonCodeToString(tempAllowListReason.mReasonCode) + + ",duration:" + tempAllowListReason.mDuration + + ",callingUid:" + tempAllowListReason.mCallingUid)) + + ">" + + "; targetSdkVersion:" + r.appInfo.targetSdkVersion + + "]"; + if (!debugInfo.equals(r.mInfoAllowStartForeground)) { + r.mLoggedInfoAllowStartForeground = false; + r.mInfoAllowStartForeground = debugInfo; + } + return ret; + } + + private @ReasonCode int shouldAllowFgsStartForegroundLocked(@ReasonCode int allowWhileInUse, + int callingPid, int callingUid, String callingPackage, + @Nullable ServiceRecord targetService) { + int ret = allowWhileInUse; + if (ret == REASON_DENIED) { + final int uidState = mAm.getUidStateLocked(callingUid); // Is the calling UID at PROCESS_STATE_TOP or above? if (uidState <= PROCESS_STATE_TOP) { - sb.append("uidState=").append(uidState); ret = getReasonCodeFromProcState(uidState); } } @@ -5557,6 +5598,14 @@ public final class ActiveServices { && instr.mHasBackgroundForegroundServiceStartsPermission) { return REASON_INSTR_BACKGROUND_FGS_PERMISSION; } + final long lastInvisibleTime = app.mState.getLastInvisibleTime(); + if (lastInvisibleTime > 0 && lastInvisibleTime < Long.MAX_VALUE) { + final long sinceLastInvisible = SystemClock.elapsedRealtime() + - lastInvisibleTime; + if (sinceLastInvisible < mAm.mConstants.mFgToBgFgsGraceDuration) { + return REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; + } + } } } return null; @@ -5608,8 +5657,10 @@ public final class ActiveServices { // NOTE this should always be the last check. if (ret == REASON_DENIED) { - if (isPackageExemptedFromFgsRestriction(r.appInfo.packageName, r.appInfo.uid) - || isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) { + if (isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) { + ret = REASON_EXEMPTED_PACKAGE; + } else if (targetService != null && isPackageExemptedFromFgsRestriction( + targetService.appInfo.packageName, targetService.appInfo.uid)) { ret = REASON_EXEMPTED_PACKAGE; } } @@ -5622,28 +5673,6 @@ public final class ActiveServices { } } - final String debugInfo = - "[callingPackage: " + callingPackage - + "; callingUid: " + callingUid - + "; uidState: " + ProcessList.makeProcStateString(uidState) - + "; intent: " + intent - + "; code:" + reasonCodeToString(ret) - + "; tempAllowListReason:<" + - (tempAllowListReason == null ? null : - (tempAllowListReason.mReason - + ",reasonCode:" - + reasonCodeToString(tempAllowListReason.mReasonCode) - + ",duration:" + tempAllowListReason.mDuration - + ",callingUid:" + tempAllowListReason.mCallingUid)) - + ">" - + "; extra:" + sb.toString() - + "; targetSdkVersion:" + r.appInfo.targetSdkVersion - + "]"; - if (!debugInfo.equals(r.mInfoAllowStartForeground)) { - r.mLoggedInfoAllowStartForeground = false; - r.mInfoAllowStartForeground = debugInfo; - } - return ret; } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index ba8f1906b0e1..5859cea2d284 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -95,6 +95,7 @@ final class ActivityManagerConstants extends ContentObserver { "process_crash_count_reset_interval"; static final String KEY_PROCESS_CRASH_COUNT_LIMIT = "process_crash_count_limit"; static final String KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION = "boot_time_temp_allowlist_duration"; + static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000; @@ -133,6 +134,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final int DEFAULT_PROCESS_CRASH_COUNT_RESET_INTERVAL = 12 * 60 * 60 * 1000; private static final int DEFAULT_PROCESS_CRASH_COUNT_LIMIT = 12; private static final int DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION = 10 * 1000; + private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000; // Flag stored in the DeviceConfig API. @@ -388,6 +390,12 @@ final class ActivityManagerConstants extends ContentObserver { */ volatile long mBootTimeTempAllowlistDuration = DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION; + /** + * The grace period in milliseconds to allow a process to start FGS from background after + * switching from foreground to background; currently it's only applicable to its activities. + */ + volatile long mFgToBgFgsGraceDuration = DEFAULT_FG_TO_BG_FGS_GRACE_DURATION; + private final ActivityManagerService mService; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -575,6 +583,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION: updateBootTimeTempAllowListDuration(); break; + case KEY_FG_TO_BG_FGS_GRACE_DURATION: + updateFgToBgFgsGraceDuration(); + break; default: break; } @@ -851,6 +862,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION); } + private void updateFgToBgFgsGraceDuration() { + mFgToBgFgsGraceDuration = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FG_TO_BG_FGS_GRACE_DURATION, + DEFAULT_FG_TO_BG_FGS_GRACE_DURATION); + } + private void updateImperceptibleKillExemptions() { IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear(); IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages); @@ -1051,6 +1069,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.println(MAX_PHANTOM_PROCESSES); pw.print(" "); pw.print(KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION); pw.print("="); pw.println(mBootTimeTempAllowlistDuration); + pw.print(" "); pw.print(KEY_FG_TO_BG_FGS_GRACE_DURATION); pw.print("="); + pw.println(mFgToBgFgsGraceDuration); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java new file mode 100644 index 000000000000..cd4180e46428 --- /dev/null +++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.am; + +import android.annotation.NonNull; +import android.annotation.SystemApi; + +/** + * Interface for in-process calls into + * {@link android.content.Context#ACTIVITY_SERVICE ActivityManager system service}. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) +public interface ActivityManagerLocal { + /** + * Checks whether an app will be able to start a foreground service or not. + * + * @param pid The process id belonging to the app to be checked. + * @param uid The UID of the app to be checked. + * @param packageName The package name of the app to be checked. + * @return whether the app will be able to start a foreground service or not. + */ + boolean canStartForegroundService(int pid, int uid, @NonNull String packageName); +} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 06a1abb72607..83cbf66ecaea 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -345,6 +345,7 @@ import com.android.server.DeviceIdleInternal; import com.android.server.DisplayThread; import com.android.server.IntentResolver; import com.android.server.IoThread; +import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; import com.android.server.LockGuard; import com.android.server.NetworkManagementInternal; @@ -2327,6 +2328,8 @@ public class ActivityManagerService extends IActivityManager.Stub mAppOpsService.publish(); Slog.d("AppOps", "AppOpsService published"); LocalServices.addService(ActivityManagerInternal.class, mInternal); + LocalManagerRegistry.addManager(ActivityManagerLocal.class, + (ActivityManagerLocal) mInternal); mActivityTaskManager.onActivityManagerInternalAdded(); mPendingIntentController.onActivityManagerInternalAdded(); mAppProfiler.onActivityManagerInternalAdded(); @@ -15086,7 +15089,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @VisibleForTesting - public final class LocalService extends ActivityManagerInternal { + public final class LocalService extends ActivityManagerInternal + implements ActivityManagerLocal { @Override public String checkContentProviderAccess(String authority, int userId) { return mCpHelper.checkContentProviderAccess(authority, userId); @@ -16008,6 +16012,13 @@ public class ActivityManagerService extends IActivityManager.Stub public void unregisterAnrController(AnrController controller) { mActivityTaskManager.unregisterAnrController(controller); } + + @Override + public boolean canStartForegroundService(int pid, int uid, @NonNull String packageName) { + synchronized (ActivityManagerService.this) { + return mServices.canStartForegroundServiceLocked(pid, uid, packageName); + } + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index e2086b01ec13..e74c936af02d 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -589,9 +589,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { Slog.w(TAG, "exception reading modem stats: " + e.getCause()); } - final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDelta; + final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas; if (mMeasuredEnergySnapshot == null || futureECRs == null) { - measuredEnergyDelta = null; + measuredEnergyDeltas = null; } else { final int voltageMv; synchronized (mStats) { @@ -610,7 +610,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { ecrs = null; } - measuredEnergyDelta = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs, voltageMv); + measuredEnergyDeltas = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs, voltageMv); } final long elapsedRealtime = SystemClock.elapsedRealtime(); @@ -633,10 +633,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } final long[] cpuClusterChargeUC; - if (measuredEnergyDelta == null) { + if (measuredEnergyDeltas == null) { cpuClusterChargeUC = null; } else { - cpuClusterChargeUC = measuredEnergyDelta.cpuClusterChargeUC; + cpuClusterChargeUC = measuredEnergyDeltas.cpuClusterChargeUC; } mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff, cpuClusterChargeUC); } @@ -650,9 +650,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mStats.updateRpmStatsLocked(elapsedRealtimeUs); } - // Inform mStats about each applicable measured energy. - if (measuredEnergyDelta != null) { - final long displayChargeUC = measuredEnergyDelta.displayChargeUC; + // Inform mStats about each applicable measured energy (unless addressed elsewhere). + if (measuredEnergyDeltas != null) { + final long displayChargeUC = measuredEnergyDeltas.displayChargeUC; if (displayChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) { // If updating, pass in what BatteryExternalStatsWorker thinks screenState is. mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC, screenState, @@ -660,19 +660,23 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } } // Inform mStats about each applicable custom energy bucket. - if (measuredEnergyDelta != null - && measuredEnergyDelta.otherTotalChargeUC != null) { + if (measuredEnergyDeltas != null + && measuredEnergyDeltas.otherTotalChargeUC != null) { // Iterate over the custom (EnergyConsumerType.OTHER) ordinals. - for (int ord = 0; ord < measuredEnergyDelta.otherTotalChargeUC.length; ord++) { - long totalEnergy = measuredEnergyDelta.otherTotalChargeUC[ord]; - SparseLongArray uidEnergies = measuredEnergyDelta.otherUidChargesUC[ord]; + for (int ord = 0; ord < measuredEnergyDeltas.otherTotalChargeUC.length; ord++) { + long totalEnergy = measuredEnergyDeltas.otherTotalChargeUC[ord]; + SparseLongArray uidEnergies = measuredEnergyDeltas.otherUidChargesUC[ord]; mStats.updateCustomMeasuredEnergyStatsLocked(ord, totalEnergy, uidEnergies); } } if (bluetoothInfo != null) { if (bluetoothInfo.isValid()) { - mStats.updateBluetoothStateLocked(bluetoothInfo, elapsedRealtime, uptime); + final long btChargeUC = measuredEnergyDeltas != null + ? measuredEnergyDeltas.bluetoothChargeUC + : MeasuredEnergySnapshot.UNAVAILABLE; + mStats.updateBluetoothStateLocked(bluetoothInfo, + btChargeUC, elapsedRealtime, uptime); } else { Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo); } @@ -684,10 +688,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if (wifiInfo != null) { if (wifiInfo.isValid()) { - // TODO: wifiEnergyDelta = measuredEnergyDelta.consumerTypeEnergyUJ - // .get(EnergyConsumerType.WIFI, MeasuredEnergySnapshot.UNAVAILABLE) - mStats.updateWifiState(extractDeltaLocked(wifiInfo) - /*, TODO: wifiEnergyDelta */, elapsedRealtime, uptime); + final long wifiChargeUC = measuredEnergyDeltas != null ? + measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE; + mStats.updateWifiState( + extractDeltaLocked(wifiInfo), wifiChargeUC, elapsedRealtime, uptime); } else { Slog.w(TAG, "wifi info is invalid: " + wifiInfo); } @@ -820,13 +824,19 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { for (int idx = 0; idx < size; idx++) { final EnergyConsumer consumer = idToConsumer.valueAt(idx); switch (consumer.type) { + case EnergyConsumerType.BLUETOOTH: + buckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH] = true; + break; + case EnergyConsumerType.CPU_CLUSTER: + buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true; + break; case EnergyConsumerType.DISPLAY: buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON] = true; buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE] = true; buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_OTHER] = true; break; - case EnergyConsumerType.CPU_CLUSTER: - buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true; + case EnergyConsumerType.WIFI: + buckets[MeasuredEnergyStats.POWER_BUCKET_WIFI] = true; break; } } @@ -864,13 +874,18 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } final IntArray energyConsumerIds = new IntArray(); + if ((flags & UPDATE_BT) != 0) { + addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.BLUETOOTH); + } if ((flags & UPDATE_CPU) != 0) { addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.CPU_CLUSTER); } if ((flags & UPDATE_DISPLAY) != 0) { addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY); } - // TODO: Wifi, Bluetooth, etc., go here + if ((flags & UPDATE_WIFI) != 0) { + addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.WIFI); + } if (energyConsumerIds.size() == 0) { return null; diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 6b9fc0718879..c3f97adbd9c3 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -17,6 +17,7 @@ package com.android.server.am; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; import android.annotation.NonNull; import android.bluetooth.BluetoothActivityEnergyInfo; @@ -1927,7 +1928,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { - mStats.updateWifiState(info, elapsedRealtime, uptime); + mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime); }); } } @@ -1945,7 +1946,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.updateBluetoothStateLocked(info, elapsedRealtime, uptime); + mStats.updateBluetoothStateLocked( + info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime); } }); } diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java index 9b2ca136bdfb..4c9ab63a100b 100644 --- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java +++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java @@ -41,7 +41,7 @@ public class MeasuredEnergySnapshot { private static final int MILLIVOLTS_PER_VOLT = 1000; - public static final long UNAVAILABLE = -1L; + public static final long UNAVAILABLE = android.os.BatteryStats.POWER_DATA_UNAVAILABLE; /** Map of {@link EnergyConsumer#id} to its corresponding {@link EnergyConsumer}. */ private final SparseArray<EnergyConsumer> mEnergyConsumers; @@ -109,12 +109,18 @@ public class MeasuredEnergySnapshot { /** Class for returning the relevant data calculated from the measured energy delta */ static class MeasuredEnergyDeltaData { - /** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */ - public long displayChargeUC = UNAVAILABLE; + /** The chargeUC for {@link EnergyConsumerType#BLUETOOTH}. */ + public long bluetoothChargeUC = UNAVAILABLE; /** The chargeUC for {@link EnergyConsumerType#CPU_CLUSTER}s. */ public long[] cpuClusterChargeUC = null; + /** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */ + public long displayChargeUC = UNAVAILABLE; + + /** The chargeUC for {@link EnergyConsumerType#WIFI}. */ + public long wifiChargeUC = UNAVAILABLE; + /** Map of {@link EnergyConsumerType#OTHER} ordinals to their total chargeUC. */ public @Nullable long[] otherTotalChargeUC = null; @@ -196,8 +202,8 @@ public class MeasuredEnergySnapshot { final long deltaChargeUC = calculateChargeConsumedUC(deltaUJ, avgVoltageMV); switch (type) { - case EnergyConsumerType.DISPLAY: - output.displayChargeUC = deltaChargeUC; + case EnergyConsumerType.BLUETOOTH: + output.bluetoothChargeUC = deltaChargeUC; break; case EnergyConsumerType.CPU_CLUSTER: @@ -207,6 +213,14 @@ public class MeasuredEnergySnapshot { output.cpuClusterChargeUC[ordinal] = deltaChargeUC; break; + case EnergyConsumerType.DISPLAY: + output.displayChargeUC = deltaChargeUC; + break; + + case EnergyConsumerType.WIFI: + output.wifiChargeUC = deltaChargeUC; + break; + case EnergyConsumerType.OTHER: if (output.otherTotalChargeUC == null) { output.otherTotalChargeUC = new long[getNumOtherOrdinals()]; @@ -215,6 +229,7 @@ public class MeasuredEnergySnapshot { output.otherTotalChargeUC[ordinal] = deltaChargeUC; output.otherUidChargesUC[ordinal] = otherUidCharges; break; + default: Slog.w(TAG, "Ignoring consumer " + consumer.name + " of unknown type " + type); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 939d35fdb5ef..5ae65ef0e4be 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1367,6 +1367,7 @@ public final class OomAdjuster { ProcessRecord app; int adj; boolean foregroundActivities; + boolean mHasVisibleActivities; int procState; int schedGroup; int appUid; @@ -1375,10 +1376,12 @@ public final class OomAdjuster { ProcessStateRecord mState; void initialize(ProcessRecord app, int adj, boolean foregroundActivities, - int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) { + boolean hasVisibleActivities, int procState, int schedGroup, int appUid, + int logUid, int processStateCurTop) { this.app = app; this.adj = adj; this.foregroundActivities = foregroundActivities; + this.mHasVisibleActivities = hasVisibleActivities; this.procState = procState; this.schedGroup = schedGroup; this.appUid = appUid; @@ -1411,6 +1414,7 @@ public final class OomAdjuster { mState.setCached(false); mState.setEmpty(false); foregroundActivities = true; + mHasVisibleActivities = true; } @Override @@ -1436,6 +1440,7 @@ public final class OomAdjuster { mState.setCached(false); mState.setEmpty(false); foregroundActivities = true; + mHasVisibleActivities = false; } @Override @@ -1468,6 +1473,7 @@ public final class OomAdjuster { mState.setCached(false); mState.setEmpty(false); foregroundActivities = true; + mHasVisibleActivities = false; } @Override @@ -1480,6 +1486,7 @@ public final class OomAdjuster { "Raise procstate to cached activity: " + app); } } + mHasVisibleActivities = false; } } @@ -1591,12 +1598,14 @@ public final class OomAdjuster { int capability = 0; boolean foregroundActivities = false; + boolean hasVisibleActivities = false; if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) { // The last app on the list is the foreground app. adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_TOP_APP; state.setAdjType("top-activity"); foregroundActivities = true; + hasVisibleActivities = true; procState = PROCESS_STATE_CUR_TOP; state.bumpAllowStartFgsState(PROCESS_STATE_TOP); if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { @@ -1672,11 +1681,12 @@ public final class OomAdjuster { // Examine all activities if not already foreground. if (!foregroundActivities && state.getCachedHasActivities()) { state.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback, - adj, foregroundActivities, procState, schedGroup, appUid, logUid, - PROCESS_STATE_CUR_TOP); + adj, foregroundActivities, hasVisibleActivities, procState, schedGroup, + appUid, logUid, PROCESS_STATE_CUR_TOP); adj = state.getCachedAdj(); foregroundActivities = state.getCachedForegroundActivities(); + hasVisibleActivities = state.getCachedHasVisibleActivities(); procState = state.getCachedProcState(); schedGroup = state.getCachedSchedGroup(); } @@ -2450,6 +2460,7 @@ public final class OomAdjuster { state.setCurrentSchedulingGroup(schedGroup); state.setCurProcState(procState); state.setCurRawProcState(procState); + state.updateLastInvisibleTime(hasVisibleActivities); state.setHasForegroundActivities(foregroundActivities); state.setCompletedAdjSeq(mAdjSeq); state.setAllowStartFgs(); diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 6d783fc63901..d97d343a1960 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -43,6 +43,7 @@ import static android.os.Process.SYSTEM_UID; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; import static com.android.server.am.ProcessRecord.TAG; +import android.annotation.ElapsedRealtimeLong; import android.app.ActivityManager; import android.content.ComponentName; import android.os.Binder; @@ -386,6 +387,16 @@ final class ProcessStateRecord { @GuardedBy("mService") private boolean mReachable; + /** + * The most recent time when the last visible activity within this process became invisible. + * + * <p> It'll be set to 0 if there is never a visible activity, or Long.MAX_VALUE if there is + * any visible activities within this process at this moment.</p> + */ + @GuardedBy("mService") + @ElapsedRealtimeLong + private long mLastInvisibleTime; + // Below are the cached task info for OomAdjuster only private static final int VALUE_INVALID = -1; private static final int VALUE_FALSE = 0; @@ -1040,18 +1051,19 @@ final class ProcessStateRecord { @GuardedBy("mService") void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback, - int adj, boolean foregroundActivities, int procState, int schedGroup, int appUid, - int logUid, int processCurTop) { + int adj, boolean foregroundActivities, boolean hasVisibleActivities, int procState, + int schedGroup, int appUid, int logUid, int processCurTop) { if (mCachedAdj != ProcessList.INVALID_ADJ) { return; } - callback.initialize(mApp, adj, foregroundActivities, procState, schedGroup, appUid, logUid, - processCurTop); + callback.initialize(mApp, adj, foregroundActivities, hasVisibleActivities, procState, + schedGroup, appUid, logUid, processCurTop); final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX, mApp.getWindowProcessController().computeOomAdjFromActivities(callback)); mCachedAdj = callback.adj; mCachedForegroundActivities = callback.foregroundActivities; + mCachedHasVisibleActivities = callback.mHasVisibleActivities ? VALUE_TRUE : VALUE_FALSE; mCachedProcState = callback.procState; mCachedSchedGroup = callback.schedGroup; @@ -1263,6 +1275,21 @@ final class ProcessStateRecord { return mAllowStartFgs; } + @GuardedBy("mService") + void updateLastInvisibleTime(boolean hasVisibleActivities) { + if (hasVisibleActivities) { + mLastInvisibleTime = Long.MAX_VALUE; + } else if (mLastInvisibleTime == Long.MAX_VALUE) { + mLastInvisibleTime = SystemClock.elapsedRealtime(); + } + } + + @GuardedBy("mService") + @ElapsedRealtimeLong + long getLastInvisibleTime() { + return mLastInvisibleTime; + } + @GuardedBy({"mService", "mProcLock"}) void dump(PrintWriter pw, String prefix, long nowUptime) { if (mReportedInteraction || mFgInteractionTime != 0) { @@ -1340,6 +1367,15 @@ final class ProcessStateRecord { TimeUtils.formatDuration(mLastTopTime, nowUptime, pw); pw.println(); } + if (mLastInvisibleTime > 0 && mLastInvisibleTime < Long.MAX_VALUE) { + pw.print(prefix); pw.print("lastInvisibleTime="); + final long elapsedRealtimeNow = SystemClock.elapsedRealtime(); + final long currentTimeNow = System.currentTimeMillis(); + final long lastInvisibleCurrentTime = + currentTimeNow - elapsedRealtimeNow + mLastInvisibleTime; + TimeUtils.dumpTimeWithDelta(pw, lastInvisibleCurrentTime, currentTimeNow); + pw.println(); + } if (mHasStartedServices) { pw.print(prefix); pw.print("hasStartedServices="); pw.println(mHasStartedServices); } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 1122f7f4115a..109ffe38f332 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -3091,14 +3091,10 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } - // This is a workaround for R QPR, new API change is not allowed. We only allow the current - // voice recognizer is also the voice interactor to noteproxy op. - final boolean isTrustVoiceServiceProxy = AppOpsManager.isTrustedVoiceServiceProxy(mContext, - proxyPackageName, code, UserHandle.getUserId(proxyUid)); final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid; final boolean isProxyTrusted = mContext.checkPermission( Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid) - == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy || isSelfBlame; + == PackageManager.PERMISSION_GRANTED || isSelfBlame; final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY; @@ -3576,14 +3572,10 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } - // This is a workaround for R QPR, new API change is not allowed. We only allow the current - // voice recognizer is also the voice interactor to noteproxy op. - final boolean isTrustVoiceServiceProxy = AppOpsManager.isTrustedVoiceServiceProxy(mContext, - proxyPackageName, code, UserHandle.getUserId(proxyUid)); final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid; final boolean isProxyTrusted = mContext.checkPermission( Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid) - == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy || isSelfBlame; + == PackageManager.PERMISSION_GRANTED || isSelfBlame; final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 68f10a5106ef..1950710a36e0 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -61,6 +61,8 @@ import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.database.ContentObserver; +import android.hardware.SensorPrivacyManager; +import android.hardware.SensorPrivacyManagerInternal; import android.hardware.hdmi.HdmiAudioSystemClient; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPlaybackClient; @@ -520,6 +522,7 @@ public class AudioService extends IAudioService.Stub /** Interface for UserManagerService. */ private final UserManagerInternal mUserManagerInternal; private final ActivityManagerInternal mActivityManagerInternal; + private final SensorPrivacyManagerInternal mSensorPrivacyManagerInternal; private final UserRestrictionsListener mUserRestrictionsListener = new AudioServiceUserRestrictionsListener(); @@ -720,9 +723,12 @@ public class AudioService extends IAudioService.Stub private String mEnabledSurroundFormats; private boolean mSurroundModeChanged; + private boolean mSupportsMicPrivacyToggle; + private boolean mMicMuteFromSwitch; private boolean mMicMuteFromApi; private boolean mMicMuteFromRestrictions; + private boolean mMicMuteFromPrivacyToggle; // caches the value returned by AudioSystem.isMicrophoneMuted() private boolean mMicMuteFromSystemCached; @@ -822,6 +828,8 @@ public class AudioService extends IAudioService.Stub mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); + mSensorPrivacyManagerInternal = + LocalServices.getService(SensorPrivacyManagerInternal.class); PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent"); @@ -831,6 +839,9 @@ public class AudioService extends IAudioService.Stub mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator(); + mSupportsMicPrivacyToggle = mContext.getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE); + // Initialize volume // Priority 1 - Android Property // Priority 2 - Audio Policy Service @@ -1106,6 +1117,16 @@ public class AudioService extends IAudioService.Stub } } + if (mSupportsMicPrivacyToggle) { + mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers( + SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> { + if (userId == getCurrentUserId()) { + mMicMuteFromPrivacyToggle = enabled; + setMicrophoneMuteNoCallerCheck(getCurrentUserId()); + } + }); + } + mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); sendMsg(mAudioHandler, @@ -3840,11 +3861,12 @@ public class AudioService extends IAudioService.Stub * @return true if microphone is reported as muted by primary HAL */ public boolean isMicrophoneMuted() { - return mMicMuteFromSystemCached; + return mMicMuteFromSystemCached && !mMicMuteFromPrivacyToggle; } private boolean isMicrophoneSupposedToBeMuted() { - return mMicMuteFromSwitch || mMicMuteFromRestrictions || mMicMuteFromApi; + return mMicMuteFromSwitch || mMicMuteFromRestrictions || mMicMuteFromApi + || mMicMuteFromPrivacyToggle; } private void setMicrophoneMuteNoCallerCheck(int userId) { @@ -7474,6 +7496,13 @@ public class AudioService extends IAudioService.Stub // the current audio focus owner is no longer valid mMediaFocusControl.discardAudioFocusOwner(); + if (mSupportsMicPrivacyToggle) { + mMicMuteFromPrivacyToggle = mSensorPrivacyManagerInternal + .isSensorPrivacyEnabled(getCurrentUserId(), + SensorPrivacyManager.Sensors.MICROPHONE); + setMicrophoneMuteNoCallerCheck(getCurrentUserId()); + } + // load volume settings for new user readAudioSettings(true /*userSwitch*/); // preserve STREAM_MUSIC volume from one user to the next. diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 1d8f210b394e..07a653fcfd3f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -137,7 +137,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal( prop.commonProps.sensorId, prop.commonProps.sensorStrength, prop.commonProps.maxEnrollmentsPerUser, false /* supportsFaceDetection */, - prop.halControlsPreview); + prop.halControlsPreview, false /* resetLockoutRequiresChallenge */); final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler, internalProp); 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 1b9bd7fd0cea..afe7f24edeaf 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 @@ -332,7 +332,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull BiometricScheduler scheduler) { mSensorProperties = new FaceSensorPropertiesInternal(sensorId, Utils.authenticatorStrengthToPropertyStrength(strength), - maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination); + maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination, + true /* resetLockoutRequiresChallenge */); mContext = context; mSensorId = sensorId; mScheduler = scheduler; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index dfec2e3e308f..0d50499bd02a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -67,7 +67,13 @@ public interface ServiceProvider { @NonNull List<FingerprintSensorPropertiesInternal> getSensorProperties(); - @NonNull + /** + * Returns the internal properties of the specified sensor, if owned by this provider. + * + * @param sensorId The ID of a fingerprint sensor, or -1 for any sensor. + * @return An object representing the internal properties of the specified sensor. + */ + @Nullable FingerprintSensorPropertiesInternal getSensorProperties(int sensorId); void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 598cc8992c2d..d798198782ea 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static android.hardware.fingerprint.FingerprintManager.SENSOR_ID_ANY; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -240,10 +242,17 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi return props; } - @NonNull + @Nullable @Override public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId) { - return mSensors.get(sensorId).getSensorProperties(); + if (mSensors.size() == 0) { + return null; + } else if (sensorId == SENSOR_ID_ANY) { + return mSensors.valueAt(0).getSensorProperties(); + } else { + final Sensor sensor = mSensors.get(sensorId); + return sensor != null ? sensor.getSensorProperties() : null; + } } private void scheduleLoadAuthenticatorIds(int sensorId) { 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 6e22a797b435..50fdc2e8a856 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 @@ -347,7 +347,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final @FingerprintSensorProperties.SensorType int sensorType = mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL : FingerprintSensorProperties.TYPE_REAR; - // resetLockout is controlled by the framework, so hardwareAuthToken is not required + // IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT + // cannot be checked final boolean resetLockoutRequiresHardwareAuthToken = false; final int maxEnrollmentsPerUser = mContext.getResources() .getInteger(R.integer.config_fingerprintMaxTemplatesPerUser); @@ -515,7 +516,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider return properties; } - @NonNull + @Nullable @Override public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId) { return mSensorProperties; diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 5bf15dc70ff9..71565301e3ed 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -80,6 +80,7 @@ import com.android.server.wm.WindowManagerInternal; import java.io.IOException; import java.io.RandomAccessFile; +import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -95,21 +96,20 @@ class HostClipboardMonitor implements Runnable { private static final String PIPE_NAME = "pipe:clipboard"; private static final String PIPE_DEVICE = "/dev/qemu_pipe"; + private static byte[] createOpenHandshake() { + // String.getBytes doesn't include the null terminator, + // but the QEMU pipe device requires the pipe service name + // to be null-terminated. + + final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1); + bits[PIPE_NAME.length()] = 0; + return bits; + } + private void openPipe() { try { - // String.getBytes doesn't include the null terminator, - // but the QEMU pipe device requires the pipe service name - // to be null-terminated. - byte[] b = new byte[PIPE_NAME.length() + 1]; - b[PIPE_NAME.length()] = 0; - System.arraycopy( - PIPE_NAME.getBytes(), - 0, - b, - 0, - PIPE_NAME.length()); mPipe = new RandomAccessFile(PIPE_DEVICE, "rw"); - mPipe.write(b); + mPipe.write(createOpenHandshake()); } catch (IOException e) { try { if (mPipe != null) mPipe.close(); @@ -173,7 +173,7 @@ public class ClipboardService extends SystemService { private static final String TAG = "ClipboardService"; private static final boolean IS_EMULATOR = - SystemProperties.getBoolean("ro.kernel.qemu", false); + SystemProperties.getBoolean("ro.boot.qemu", false); // DeviceConfig properties private static final String PROPERTY_SHOW_ACCESS_NOTIFICATIONS = "show_access_notifications"; diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index 7a5abf807fbb..702434ba07b7 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -127,13 +127,17 @@ public class DnsManager { private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8; private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64; - public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) { - final String mode = ConnectivityManager.getPrivateDnsMode(cr); + /** + * Get PrivateDnsConfig. + */ + public static PrivateDnsConfig getPrivateDnsConfig(Context context) { + final String mode = ConnectivityManager.getPrivateDnsMode(context); final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode); if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) { - final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER); + final String specifier = getStringSetting(context.getContentResolver(), + PRIVATE_DNS_SPECIFIER); return new PrivateDnsConfig(specifier, null); } @@ -268,7 +272,7 @@ public class DnsManager { } public PrivateDnsConfig getPrivateDnsConfig() { - return getPrivateDnsConfig(mContentResolver); + return getPrivateDnsConfig(mContext); } public void removeNetwork(Network network) { diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 803cc9d31c35..e44dcf5975f1 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -35,6 +35,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkMonitorManager; import android.net.NetworkRequest; +import android.net.NetworkScore; import android.net.NetworkStateSnapshot; import android.net.QosCallbackException; import android.net.QosFilter; @@ -302,8 +303,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // validated). private boolean mInactive; - // This represents the quality of the network with no clear scale. - private int mScore; + // This represents the quality of the network. + private NetworkScore mScore; // The list of NetworkRequests being satisfied by this Network. private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>(); @@ -338,7 +339,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { private final QosCallbackTracker mQosCallbackTracker; public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info, - @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context, + @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, + @NonNull NetworkScore score, Context context, Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd, IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid, QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) { @@ -603,9 +605,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } @Override - public void sendScore(int score) { - mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED, score, 0, - new Pair<>(NetworkAgentInfo.this, null)).sendToTarget(); + public void sendScore(@NonNull final NetworkScore score) { + mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED, + new Pair<>(NetworkAgentInfo.this, score)).sendToTarget(); } @Override @@ -717,6 +719,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { break; case LISTEN: + case LISTEN_FOR_BEST: case TRACK_DEFAULT: case TRACK_SYSTEM_DEFAULT: break; @@ -857,7 +860,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE; } - int score = mScore; + int score = mScore.getLegacyInt(); if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) { score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY; } @@ -886,7 +889,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return getCurrentScore(true); } - public void setScore(final int score) { + public void setScore(final NetworkScore score) { mScore = score; } diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java index 5e6b9f39b40a..2e51be39bfae 100644 --- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java +++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java @@ -36,7 +36,7 @@ import android.text.TextUtils; import android.util.Pair; import com.android.internal.util.IndentingPrintWriter; -import com.android.internal.util.TrafficStatsConstants; +import com.android.net.module.util.NetworkStackConstants; import libcore.io.IoUtils; @@ -446,7 +446,7 @@ public class NetworkDiagnostics { int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort) throws ErrnoException, IOException { final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_PROBE); + NetworkStackConstants.TAG_SYSTEM_PROBE); try { mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol); } finally { @@ -745,7 +745,7 @@ public class NetworkDiagnostics { if (ensureMeasurementNecessary()) return; // No need to restore the tag, since this thread is only used for this measurement. - TrafficStats.getAndSetThreadStatsTag(TrafficStatsConstants.TAG_SYSTEM_PROBE); + TrafficStats.getAndSetThreadStatsTag(NetworkStackConstants.TAG_SYSTEM_PROBE); try (SSLSocket sslSocket = setupSSLSocket()) { sendDoTProbe(sslSocket); diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 217f1cd56598..a8b0994402e8 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -42,6 +42,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.SystemService; import com.android.server.policy.DeviceStatePolicyImpl; @@ -447,6 +448,9 @@ public final class DeviceStateManagerService extends SystemService { } } + FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_STATE_CHANGED, + newState.getIdentifier(), !mCommittedState.isPresent()); + mCommittedState = Optional.of(newState); mPendingState = Optional.empty(); updatePendingStateLocked(); diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 06010f51e231..251b57947195 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -110,6 +110,7 @@ public class BrightnessTracker { private static final String ATTR_TIMESTAMP = "timestamp"; private static final String ATTR_PACKAGE_NAME = "packageName"; private static final String ATTR_USER = "user"; + private static final String ATTR_UNIQUE_DISPLAY_ID = "uniqueDisplayId"; private static final String ATTR_LUX = "lux"; private static final String ATTR_LUX_TIMESTAMPS = "luxTimestamps"; private static final String ATTR_BATTERY_LEVEL = "batteryLevel"; @@ -217,6 +218,9 @@ public class BrightnessTracker { } private void backgroundStart(float initialBrightness) { + if (DEBUG) { + Slog.d(TAG, "Background start"); + } readEvents(); readAmbientBrightnessStats(); @@ -311,7 +315,7 @@ public class BrightnessTracker { */ public void notifyBrightnessChanged(float brightness, boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness, - boolean isDefaultBrightnessConfig) { + boolean isDefaultBrightnessConfig, String uniqueDisplayId) { if (DEBUG) { Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)", brightness, userInitiated)); @@ -319,13 +323,13 @@ public class BrightnessTracker { Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED, userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness, powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig, - mInjector.currentTimeMillis())); + mInjector.currentTimeMillis(), uniqueDisplayId)); m.sendToTarget(); } private void handleBrightnessChanged(float brightness, boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness, - boolean isDefaultBrightnessConfig, long timestamp) { + boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId) { BrightnessChangeEvent.Builder builder; synchronized (mDataCollectionLock) { @@ -350,6 +354,7 @@ public class BrightnessTracker { builder.setPowerBrightnessFactor(powerBrightnessFactor); builder.setUserBrightnessPoint(isUserSetBrightness); builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig); + builder.setUniqueDisplayId(uniqueDisplayId); final int readingCount = mLastSensorReadings.size(); if (readingCount == 0) { @@ -562,6 +567,7 @@ public class BrightnessTracker { out.attributeLong(null, ATTR_TIMESTAMP, toWrite[i].timeStamp); out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName); out.attributeInt(null, ATTR_USER, userSerialNo); + out.attribute(null, ATTR_UNIQUE_DISPLAY_ID, toWrite[i].uniqueDisplayId); out.attributeFloat(null, ATTR_BATTERY_LEVEL, toWrite[i].batteryLevel); out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode); out.attributeInt(null, ATTR_COLOR_TEMPERATURE, @@ -646,6 +652,8 @@ public class BrightnessTracker { builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME)); builder.setUserId(mInjector.getUserId(mUserManager, parser.getAttributeInt(null, ATTR_USER))); + builder.setUniqueDisplayId( + parser.getAttributeValue(null, ATTR_UNIQUE_DISPLAY_ID)); builder.setBatteryLevel(parser.getAttributeFloat(null, ATTR_BATTERY_LEVEL)); builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE)); builder.setColorTemperature( @@ -980,7 +988,8 @@ public class BrightnessTracker { boolean userInitiatedChange = (msg.arg1 == 1); handleBrightnessChanged(values.brightness, userInitiatedChange, values.powerBrightnessFactor, values.isUserSetBrightness, - values.isDefaultBrightnessConfig, values.timestamp); + values.isDefaultBrightnessConfig, values.timestamp, + values.uniqueDisplayId); break; case MSG_START_SENSOR_LISTENER: startSensorListener(); @@ -1007,20 +1016,22 @@ public class BrightnessTracker { } private static class BrightnessChangeValues { - final float brightness; - final float powerBrightnessFactor; - final boolean isUserSetBrightness; - final boolean isDefaultBrightnessConfig; - final long timestamp; + public final float brightness; + public final float powerBrightnessFactor; + public final boolean isUserSetBrightness; + public final boolean isDefaultBrightnessConfig; + public final long timestamp; + public final String uniqueDisplayId; BrightnessChangeValues(float brightness, float powerBrightnessFactor, boolean isUserSetBrightness, boolean isDefaultBrightnessConfig, - long timestamp) { + long timestamp, String uniqueDisplayId) { this.brightness = brightness; this.powerBrightnessFactor = powerBrightnessFactor; this.isUserSetBrightness = isUserSetBrightness; this.isDefaultBrightnessConfig = isDefaultBrightnessConfig; this.timestamp = timestamp; + this.uniqueDisplayId = uniqueDisplayId; } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 96a74161a6eb..82ca820ec4d7 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -376,6 +376,8 @@ public final class DisplayManagerService extends SystemService { private final ColorSpace mWideColorSpace; private SensorManager mSensorManager; + private BrightnessTracker mBrightnessTracker; + // Whether minimal post processing is allowed by the user. @GuardedBy("mSyncRoot") @@ -1162,7 +1164,7 @@ public final class DisplayManagerService extends SystemService { DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); if (dpc != null) { - dpc.onDisplayChanged(); + dpc.onDisplayChangedLocked(); } } @@ -1851,7 +1853,10 @@ public final class DisplayManagerService extends SystemService { for (int i = 0; i < displayPowerControllerCount; i++) { mDisplayPowerControllers.valueAt(i).dump(pw); } - + if (mBrightnessTracker != null) { + pw.println(); + mBrightnessTracker.dump(pw); + } pw.println(); mPersistentDataStore.dump(pw); } @@ -1937,9 +1942,12 @@ public final class DisplayManagerService extends SystemService { // initPowerManagement has not yet been called. return; } + if (mBrightnessTracker == null) { + mBrightnessTracker = new BrightnessTracker(mContext, null); + } final DisplayPowerController displayPowerController = new DisplayPowerController( mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager, - mDisplayBlanker, display); + mDisplayBlanker, display, mBrightnessTracker); mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index e44ecaca3fa0..7110d3e6a7c1 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -174,6 +174,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The ID of the LogicalDisplay tied to this DisplayPowerController. private final int mDisplayId; + // The unique ID of the primary display device currently tied to this logical display + private String mUniqueDisplayId; + // Tracker for brightness changes. @Nullable private final BrightnessTracker mBrightnessTracker; @@ -416,16 +419,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call */ public DisplayPowerController(Context context, DisplayPowerCallbacks callbacks, Handler handler, - SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay) { + SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay, + BrightnessTracker brightnessTracker) { mLogicalDisplay = logicalDisplay; mDisplayId = mLogicalDisplay.getDisplayIdLocked(); mHandler = new DisplayControllerHandler(handler.getLooper()); if (mDisplayId == Display.DEFAULT_DISPLAY) { - mBrightnessTracker = new BrightnessTracker(context, null); mBatteryStats = BatteryStatsService.getService(); } else { - mBrightnessTracker = null; mBatteryStats = null; } @@ -435,6 +437,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class); mBlanker = blanker; mContext = context; + mBrightnessTracker = brightnessTracker; PowerManager pm = context.getSystemService(PowerManager.class); @@ -756,8 +759,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call * when displays get swapped on foldable devices. For example, different brightness properties * of each display need to be properly reflected in AutomaticBrightnessController. */ - public void onDisplayChanged() { + public void onDisplayChangedLocked() { // TODO: b/175821789 - Support high brightness on multiple (folding) displays + + mUniqueDisplayId = mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); } /** @@ -780,10 +785,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.stop(); } - if (mBrightnessTracker != null) { - mBrightnessTracker.stop(); - } - mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); } } @@ -1899,7 +1900,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call : 1.0f; mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated, powerFactor, hadUserDataPoint, - mAutomaticBrightnessController.isDefaultConfig()); + mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId); } } @@ -2067,11 +2068,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.dump(pw); } - if (mBrightnessTracker != null) { - pw.println(); - mBrightnessTracker.dump(pw); - } - pw.println(); if (mDisplayWhiteBalanceController != null) { mDisplayWhiteBalanceController.dump(pw); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index aaec89afa94c..2546118f1cc7 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -853,14 +853,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { // Do not lock when calling these SurfaceControl methods because they are sync // operations that may block for a while when setting display power mode. mSurfaceControlProxy.setDesiredDisplayModeSpecs(displayToken, modeSpecs); - - final int sfActiveModeId = mSurfaceControlProxy - .getDynamicDisplayInfo(displayToken).activeDisplayModeId; - synchronized (getSyncRoot()) { - if (updateActiveModeLocked(sfActiveModeId)) { - updateDeviceInfoLocked(); - } - } } @Override diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index e6e2f9631d45..03a83380246f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -431,6 +431,13 @@ public class HdmiControlService extends SystemService { private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer(); + @VisibleForTesting HdmiControlService(Context context, List<Integer> deviceTypes) { + super(context); + mLocalDevices = deviceTypes; + mSettingsObserver = new SettingsObserver(mHandler); + mHdmiCecConfig = new HdmiCecConfig(context); + } + public HdmiControlService(Context context) { super(context); List<Integer> deviceTypes = HdmiProperties.device_type(); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index e9c3ec392d38..ffb532ebcca4 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2923,8 +2923,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub dismissImeOnBackKeyPressed = ((vis & InputMethodService.IME_VISIBLE) != 0); break; } - mWindowManagerInternal.updateInputMethodWindowStatus(token, - (vis & InputMethodService.IME_VISIBLE) != 0, dismissImeOnBackKeyPressed); + mWindowManagerInternal.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed); } @BinderThread diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java index b13c307497d9..7e5e427cf0a1 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.pm.PackageManager; import android.text.TextUtils; import android.util.ArraySet; -import android.util.Log; import android.util.Printer; import android.util.Slog; import android.view.inputmethod.InputMethodInfo; @@ -502,7 +501,7 @@ final class InputMethodSubtypeSwitchingController { public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) { if (mController == null) { if (DEBUG) { - Log.e(TAG, "mController shouldn't be null."); + Slog.e(TAG, "mController shouldn't be null."); } return; } @@ -520,7 +519,7 @@ final class InputMethodSubtypeSwitchingController { InputMethodSubtype subtype) { if (mController == null) { if (DEBUG) { - Log.e(TAG, "mController shouldn't be null."); + Slog.e(TAG, "mController shouldn't be null."); } return null; } diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java index e1c011d821a7..c8c212b3109c 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java @@ -47,9 +47,11 @@ import android.util.proto.ProtoOutputStream; import com.android.server.location.ClientBrokerProto; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -205,14 +207,19 @@ public class ContextHubClientBroker extends IContextHubClient.Stub * allowed to communicate over that channel. A channel is defined to have been opened if the * client has sent or received messages from the particular nanoapp. */ - private final Map<Long, Integer> mMessageChannelNanoappIdMap = - new ConcurrentHashMap<Long, Integer>(); + private final Map<Long, Integer> mMessageChannelNanoappIdMap = new ConcurrentHashMap<>(); + + /** + * Set containing all nanoapps that have been forcefully transitioned to the denied + * authorization state (via CLI) to ensure they don't transition back to the granted state + * later if, for example, a permission check is performed due to another nanoapp + */ + private final Set<Long> mForceDeniedNapps = new HashSet<>(); /** * Map containing all nanoapps that have active auth state denial timers. */ - private final Map<Long, AuthStateDenialTimer> mNappToAuthTimerMap = - new ConcurrentHashMap<Long, AuthStateDenialTimer>(); + private final Map<Long, AuthStateDenialTimer> mNappToAuthTimerMap = new ConcurrentHashMap<>(); /** * Callback used to obtain the latest set of nanoapp permissions and verify this client has @@ -637,7 +644,8 @@ public class ContextHubClientBroker extends IContextHubClient.Stub private int updateNanoAppAuthState( long nanoAppId, List<String> nanoappPermissions, boolean gracePeriodExpired) { return updateNanoAppAuthState( - nanoAppId, nanoappPermissions, gracePeriodExpired, false /* forceDenied */); + nanoAppId, nanoappPermissions, gracePeriodExpired, + mForceDeniedNapps.contains(nanoAppId) /* forceDenied */); } /** @@ -679,6 +687,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub // any state -> DENIED if "forceDenied" is true if (forceDenied) { newAuthState = AUTHORIZATION_DENIED; + mForceDeniedNapps.add(nanoAppId); } else if (gracePeriodExpired) { if (curAuthState == AUTHORIZATION_DENIED_GRACE_PERIOD) { newAuthState = AUTHORIZATION_DENIED; diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 0a074e1e7c50..b10d56b62acc 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -1068,6 +1068,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) { try { + if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { + final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction()) + + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode()); + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); + } if (asSystemService) { mCb.onMediaButton(mContext.getPackageName(), Process.myPid(), Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb); @@ -1085,6 +1091,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent) { try { + if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { + final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction()) + + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode()); + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); + } if (asSystemService) { mCb.onMediaButton(mContext.getPackageName(), Process.myPid(), Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), 0, null); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 23d84298b41e..46ece74180fe 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -63,6 +63,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Message; +import android.os.PowerExemptionManager; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; @@ -82,9 +83,11 @@ import android.view.ViewConfiguration; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.server.LocalManagerRegistry; import com.android.server.SystemService; import com.android.server.Watchdog; import com.android.server.Watchdog.Monitor; +import com.android.server.am.ActivityManagerLocal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -114,6 +117,13 @@ public class MediaSessionService extends SystemService implements Monitor { */ private static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver"; + /** + * Denotes the duration during which an app receiving a media session callback will be + * exempted from FGS-from-BG restriction and so will be allowed to start an FGS even if it is + * in the background state while it receives a media session callback. + */ + private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000; + private final Context mContext; private final SessionManagerImpl mSessionManagerImpl; private final MessageHandler mHandler = new MessageHandler(); @@ -136,6 +146,7 @@ public class MediaSessionService extends SystemService implements Monitor { private KeyguardManager mKeyguardManager; private AudioManager mAudioManager; private boolean mHasFeatureLeanback; + private ActivityManagerLocal mActivityManagerLocal; // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) // It's always not null after the MediaSessionService is started. @@ -219,6 +230,8 @@ public class MediaSessionService extends SystemService implements Monitor { final IntentFilter filter = new IntentFilter( NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED); mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter); + + mActivityManagerLocal = LocalManagerRegistry.getManager(ActivityManagerLocal.class); } @Override @@ -538,6 +551,26 @@ public class MediaSessionService extends SystemService implements Monitor { throw new IllegalArgumentException("packageName is not owned by the calling process"); } + void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage, + int callingPid, int callingUid, String callingPackage, String reason) { + final long token = Binder.clearCallingIdentity(); + try { + enforcePackageName(callingPackage, callingUid); + if (targetUid != callingUid && mActivityManagerLocal.canStartForegroundService( + callingPid, callingUid, callingPackage)) { + final Context userContext = mContext.createContextAsUser( + UserHandle.of(UserHandle.getUserId(targetUid)), /* flags= */ 0); + final PowerExemptionManager powerExemptionManager = userContext.getSystemService( + PowerExemptionManager.class); + powerExemptionManager.addToTemporaryAllowList(targetPackage, + FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS, + PowerExemptionManager.REASON_MEDIA_SESSION_CALLBACK, reason); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + /** * Checks a caller's authorization to register an IRemoteControlDisplay. * Authorization is granted if one of the following is true: diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d5a9e3c0d4f8..e58836659189 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -10635,17 +10635,17 @@ public class NotificationManagerService extends SystemService { return true; } } - String toastMessage = "Indirect activity start from " + packageName; String logcatMessage = "Indirect notification activity start (trampoline) from " + packageName; - + // Call to toast() method is posted to mHandler below to offload PM lookup from the + // activity start path if (CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid)) { - toast(toastMessage + " blocked."); + mHandler.post(() -> toast(packageName, uid, /* blocked */ true)); Slog.e(TAG, logcatMessage + " blocked"); return false; } else { if (mPackagesShown.add(packageName)) { - toast(toastMessage + ". This will be blocked in S."); + mHandler.post(() -> toast(packageName, uid, /* blocked */ false)); } Slog.w(TAG, logcatMessage + ", this should be avoided for performance reasons"); return true; @@ -10661,10 +10661,19 @@ public class NotificationManagerService extends SystemService { && !CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid); } - private void toast(String message) { - mUiHandler.post(() -> - Toast.makeText(getUiContext(), message + "\nSee g.co/dev/trampolines.", - Toast.LENGTH_LONG).show()); + private void toast(String packageName, int uid, boolean blocked) { + final CharSequence label; + try { + label = mPackageManagerClient.getApplicationLabel( + mPackageManager.getApplicationInfo(packageName, 0, + UserHandle.getUserId(uid))); + } catch (RemoteException e) { + Slog.e(TAG, "Unexpected exception obtaining app label from PackageManager", e); + return; + } + mUiHandler.post(() -> Toast.makeText(getUiContext(), + label + " launch " + (blocked ? "blocked" : "will be blocked") + + "\ng.co/dev/trampolines", Toast.LENGTH_LONG).show()); } } } 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 e3ccb7521b58..27bf8a13766a 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -737,10 +737,12 @@ final class DefaultPermissionGrantPolicy { } } if (locationExtraPackageNames != null) { - // Also grant location permission to location extra packages. + // Also grant location and activity recognition permission to location extra packages. for (String packageName : locationExtraPackageNames) { grantPermissionsToSystemPackage(pm, packageName, userId, ALWAYS_LOCATION_PERMISSIONS); + grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId, + ACTIVITY_RECOGNITION_PERMISSIONS); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 1e4907140fa2..d0aa28b441a0 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -4251,12 +4251,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } mPowerManager.wakeUp(wakeTime, reason, details); - - // Turn on the connected TV and switch HDMI input if we're a HDMI playback device. - final HdmiControl hdmiControl = getHdmiControl(); - if (hdmiControl != null) { - hdmiControl.turnOnTv(); - } return true; } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 370d921de2af..75f06e5e9088 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -90,7 +90,6 @@ import android.system.Os; import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; -import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -2447,8 +2446,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub @Override public void removeOnLocalColorsChangedListener( - @NonNull ILocalWallpaperColorConsumer callback, int which, int userId, - int displayId) throws RemoteException { + @NonNull ILocalWallpaperColorConsumer callback, List<RectF> removeAreas, int which, + int userId, int displayId) throws RemoteException { if (which != FLAG_LOCK && which != FLAG_SYSTEM) { throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM"); } @@ -2457,43 +2456,45 @@ public class WallpaperManagerService extends IWallpaperManager.Stub throw new SecurityException("calling user id does not match"); } final long identity = Binder.clearCallingIdentity(); - ArrayList<RectF> removeAreas = new ArrayList<>(); - ArrayList<Pair<RemoteCallbackList, ILocalWallpaperColorConsumer>> - callbacksToRemove = new ArrayList<>(); + ArrayList<RectF> purgeAreas = new ArrayList<>(); + IBinder binder = callback.asBinder(); try { synchronized (mLock) { - ArraySet<RectF> areas = mLocalColorCallbackAreas.remove(callback.asBinder()); - mLocalColorCallbackDisplayId.remove(callback.asBinder()); - if (areas == null) areas = new ArraySet<>(); - for (RectF area : areas) { - RemoteCallbackList callbacks = mLocalColorAreaCallbacks.get(area); - if (callbacks == null) continue; - callbacksToRemove.add(new Pair<>(callbacks, callback)); - if (callbacks.getRegisteredCallbackCount() == 0) { - mLocalColorAreaCallbacks.remove(area); - removeAreas.add(area); - } - ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId); - if (displayAreas != null) { - displayAreas.remove(area); + ArraySet<RectF> currentAreas = mLocalColorCallbackAreas.get(binder); + if (currentAreas == null) return; + currentAreas.removeAll(removeAreas); + if (currentAreas.size() == 0) { + mLocalColorCallbackDisplayId.remove(binder); + for (RectF removeArea : removeAreas) { + RemoteCallbackList<ILocalWallpaperColorConsumer> remotes = + mLocalColorAreaCallbacks.get(removeArea); + if (remotes == null) continue; + remotes.unregister(callback); + if (remotes.getRegisteredCallbackCount() == 0) { + mLocalColorAreaCallbacks.remove(removeArea); + purgeAreas.add(removeArea); + ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId); + if (displayAreas != null) { + displayAreas.remove(removeArea); + if (displayAreas.size() == 0) { + mLocalColorDisplayIdAreas.remove(displayId); + } + } + } } } } - for (int i = 0; i < callbacksToRemove.size(); i++) { - Pair<RemoteCallbackList, ILocalWallpaperColorConsumer> - pair = callbacksToRemove.get(i); - pair.first.unregister(pair.second); - } + } catch (Exception e) { // ignore any exception } finally { Binder.restoreCallingIdentity(identity); } - if (removeAreas.size() == 0) return; + if (purgeAreas.size() == 0) return; IWallpaperEngine engine = getEngine(which, userId, displayId); if (engine == null) return; - engine.removeLocalColorsAreas(removeAreas); + engine.removeLocalColorsAreas(purgeAreas); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index c830ba9b61dd..c5115b283f0a 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -65,6 +65,7 @@ import android.os.Trace; import android.service.voice.VoiceInteractionManagerInternal; import android.util.Slog; import android.view.RemoteAnimationDefinition; +import android.window.SizeConfigurationBuckets; import com.android.internal.app.AssistUtils; import com.android.internal.policy.IKeyguardDismissCallback; @@ -74,8 +75,6 @@ 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. * @@ -244,16 +243,14 @@ class ActivityClientController extends IActivityClientController.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)); + public void reportSizeConfigurations(IBinder token, + SizeConfigurationBuckets sizeConfigurations) { + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s", + token, sizeConfigurations); synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); if (r != null) { - r.setSizeConfigurations(horizontalSizeConfiguration, verticalSizeConfigurations, - smallestSizeConfigurations); + r.setSizeConfigurations(sizeConfigurations); } } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index db3d7ad0c398..a909c6d119e8 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -316,6 +316,7 @@ import android.view.WindowManager.LayoutParams; import android.view.WindowManager.TransitionOldType; import android.view.animation.Animation; import android.window.IRemoteTransition; +import android.window.SizeConfigurationBuckets; import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.TaskSnapshot; import android.window.WindowContainerToken; @@ -563,12 +564,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The locusId associated with this activity, if set. private LocusId mLocusId; - // These configurations are collected from application's resources based on size-sensitive - // qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800 - // and drawable-sw400dp will be added to both as 400. - private int[] mVerticalSizeConfigurations; - private int[] mHorizontalSizeConfigurations; - private int[] mSmallestSizeConfigurations; + private SizeConfigurationBuckets mSizeConfigurations; /** * The precomputed display insets for resolving configuration. It will be non-null if @@ -664,13 +660,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // naturally. private boolean mInSizeCompatModeForBounds = false; - // Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed - // orientation then aspect ratio restrictions are also already respected. + // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed + // for fixed orientation. If not null, they are used as parent container in + // resolveSizeCompatModeConfiguration and in a constructor of CompatDisplayInsets. If + // letterboxed due to fixed orientation then aspect ratio restrictions are also respected. // This happens when an activity has fixed orientation which doesn't match orientation of the // parent because a display is ignoring orientation request or fixed to user rotation. // See WindowManagerService#getIgnoreOrientationRequest and // WindowManagerService#getFixedToUserRotation for more context. - private boolean mIsLetterboxedForFixedOrientationAndAspectRatio = false; + @Nullable + private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio; // activity is not displayed? // TODO: rename to mNoDisplay @@ -1078,11 +1077,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pw.println(prefix + "supportsEnterPipOnTaskSwitch: " + supportsEnterPipOnTaskSwitch); } - if (info.maxAspectRatio != 0) { - pw.println(prefix + "maxAspectRatio=" + info.maxAspectRatio); + if (info.getMaxAspectRatio() != 0) { + pw.println(prefix + "maxAspectRatio=" + info.getMaxAspectRatio()); + } + if (info.getMinAspectRatio() != 0) { + pw.println(prefix + "minAspectRatio=" + info.getMinAspectRatio()); } - if (info.minAspectRatio != 0) { - pw.println(prefix + "minAspectRatio=" + info.minAspectRatio); + if (info.getMinAspectRatio() != info.getManifestMinAspectRatio()) { + // Log the fact that we've overridden the min aspect ratio from the manifest + pw.println(prefix + "manifestMinAspectRatio=" + + info.getManifestMinAspectRatio()); } pw.println(prefix + "supportsSizeChanges=" + ActivityInfo.sizeChangesSupportModeToString(info.supportsSizeChanges())); @@ -1168,52 +1172,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A info.applicationInfo = aInfo; } - private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) { - return crossesSizeThreshold(mHorizontalSizeConfigurations, firstDp, secondDp); - } - - private boolean crossesVerticalSizeThreshold(int firstDp, int secondDp) { - return crossesSizeThreshold(mVerticalSizeConfigurations, firstDp, secondDp); - } - - private boolean crossesSmallestSizeThreshold(int firstDp, int secondDp) { - return crossesSizeThreshold(mSmallestSizeConfigurations, firstDp, secondDp); - } - - /** - * The purpose of this method is to decide whether the activity needs to be relaunched upon - * changing its size. In most cases the activities don't need to be relaunched, if the resize - * is small, all the activity content has to do is relayout itself within new bounds. There are - * cases however, where the activity's content would be completely changed in the new size and - * the full relaunch is required. - * - * The activity will report to us vertical and horizontal thresholds after which a relaunch is - * required. These thresholds are collected from the application resource qualifiers. For - * example, if application has layout-w600dp resource directory, then it needs a relaunch when - * we resize from width of 650dp to 550dp, as it crosses the 600dp threshold. However, if - * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side - * of the threshold. - */ - private static boolean crossesSizeThreshold(int[] thresholds, int firstDp, - int secondDp) { - if (thresholds == null) { - return false; - } - for (int i = thresholds.length - 1; i >= 0; i--) { - final int threshold = thresholds[i]; - if ((firstDp < threshold && secondDp >= threshold) - || (firstDp >= threshold && secondDp < threshold)) { - return true; - } - } - return false; - } - - void setSizeConfigurations(int[] horizontalSizeConfiguration, - int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) { - mHorizontalSizeConfigurations = horizontalSizeConfiguration; - mVerticalSizeConfigurations = verticalSizeConfigurations; - mSmallestSizeConfigurations = smallestSizeConfigurations; + void setSizeConfigurations(SizeConfigurationBuckets sizeConfigurations) { + mSizeConfigurations = sizeConfigurations; } private void scheduleActivityMovedToDisplay(int displayId, Configuration config) { @@ -6863,7 +6823,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. - private void updateCompatDisplayInsets(@Nullable Rect fixedOrientationBounds) { + private void updateCompatDisplayInsets() { if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) { // The override configuration is set only once in size compatibility mode. return; @@ -6891,7 +6851,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The role of CompatDisplayInsets is like the override bounds. mCompatDisplayInsets = - new CompatDisplayInsets(mDisplayContent, this, fixedOrientationBounds); + new CompatDisplayInsets( + mDisplayContent, this, mLetterboxBoundsForFixedOrientationAndAspectRatio); } @VisibleForTesting @@ -6945,8 +6906,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A || windowingMode == WINDOWING_MODE_FULLSCREEN) { resolveFixedOrientationConfiguration(newParentConfiguration); } - final Rect fixedOrientationBounds = isLetterboxedForFixedOrientationAndAspectRatio() - ? new Rect(resolvedConfig.windowConfiguration.getBounds()) : null; if (mCompatDisplayInsets != null) { resolveSizeCompatModeConfiguration(newParentConfiguration); @@ -6966,7 +6925,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (mVisibleRequested) { - updateCompatDisplayInsets(fixedOrientationBounds); + updateCompatDisplayInsets(); } // TODO(b/175212232): Consolidate position logic from each "resolve" method above here. @@ -7001,7 +6960,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * WindowManagerService#getIgnoreOrientationRequest} for more context. */ boolean isLetterboxedForFixedOrientationAndAspectRatio() { - return mIsLetterboxedForFixedOrientationAndAspectRatio; + return mLetterboxBoundsForFixedOrientationAndAspectRatio != null; } /** @@ -7012,7 +6971,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * in this methiod. */ private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) { - mIsLetterboxedForFixedOrientationAndAspectRatio = false; + mLetterboxBoundsForFixedOrientationAndAspectRatio = null; if (handlesOrientationChangeFromDescendant()) { // No need to letterbox because of fixed orientation. Display will handle // fixed-orientation requests. @@ -7053,8 +7012,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in // order to use the extra available space. - final float maxAspectRatio = info.maxAspectRatio; - final float minAspectRatio = info.minAspectRatio; + final float maxAspectRatio = info.getMaxAspectRatio(); + final float minAspectRatio = info.getMinAspectRatio(); if (aspect > maxAspectRatio && maxAspectRatio != 0) { aspect = maxAspectRatio; } else if (aspect < minAspectRatio) { @@ -7089,7 +7048,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Calculate app bounds using fixed orientation bounds because they will be needed later // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}. task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig); - mIsLetterboxedForFixedOrientationAndAspectRatio = true; + mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds); } /** @@ -7291,21 +7250,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The rest of the condition is that only one side is smaller than the container, but it // still needs to exclude the cases where the size is limited by the fixed aspect ratio. - if (info.maxAspectRatio > 0) { + if (info.getMaxAspectRatio() > 0) { final float aspectRatio = (0.5f + Math.max(appWidth, appHeight)) / Math.min(appWidth, appHeight); - if (aspectRatio >= info.maxAspectRatio) { + if (aspectRatio >= info.getMaxAspectRatio()) { // The current size has reached the max aspect ratio. return false; } } - if (info.minAspectRatio > 0) { + if (info.getMinAspectRatio() > 0) { // The activity should have at least the min aspect ratio, so this checks if the // container still has available space to provide larger aspect ratio. final float containerAspectRatio = (0.5f + Math.max(containerAppWidth, containerAppHeight)) / Math.min(containerAppWidth, containerAppHeight); - if (containerAspectRatio <= info.minAspectRatio) { + if (containerAspectRatio <= info.getMinAspectRatio()) { // The long side has reached the parent. return false; } @@ -7472,9 +7431,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private void applyAspectRatio(Rect outBounds, Rect containingAppBounds, Rect containingBounds) { - final float maxAspectRatio = info.maxAspectRatio; + final float maxAspectRatio = info.getMaxAspectRatio(); final Task rootTask = getRootTask(); - final float minAspectRatio = info.minAspectRatio; + final float minAspectRatio = info.getMinAspectRatio(); if (task == null || rootTask == null || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()) @@ -7635,6 +7594,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display " + "unchanged in %s", this); + // It's possible that resolveOverrideConfiguration was called before mVisibleRequested + // became true and mCompatDisplayInsets may not have been created so ensure + // that mCompatDisplayInsets is created here. + if (mVisibleRequested) { + updateCompatDisplayInsets(); + } return true; } @@ -7784,26 +7749,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Determine what has changed. May be nothing, if this is a config that has come back from // the app after going idle. In that case we just want to leave the official config object // now in the activity and do nothing else. - final Configuration currentConfig = getConfiguration(); - int changes = lastReportedConfig.diff(currentConfig); - // We don't want to use size changes if they don't cross boundaries that are important to - // the app. - if ((changes & CONFIG_SCREEN_SIZE) != 0) { - final boolean crosses = crossesHorizontalSizeThreshold(lastReportedConfig.screenWidthDp, - currentConfig.screenWidthDp) - || crossesVerticalSizeThreshold(lastReportedConfig.screenHeightDp, - currentConfig.screenHeightDp); - if (!crosses) { - changes &= ~CONFIG_SCREEN_SIZE; - } - } - if ((changes & CONFIG_SMALLEST_SCREEN_SIZE) != 0) { - final int oldSmallest = lastReportedConfig.smallestScreenWidthDp; - final int newSmallest = currentConfig.smallestScreenWidthDp; - if (!crossesSmallestSizeThreshold(oldSmallest, newSmallest)) { - changes &= ~CONFIG_SMALLEST_SCREEN_SIZE; - } - } + int changes = lastReportedConfig.diff(getConfiguration()); + changes = SizeConfigurationBuckets.filterDiff( + changes, lastReportedConfig, getConfiguration(), mSizeConfigurations); // We don't want window configuration to cause relaunches. if ((changes & CONFIG_WINDOW_CONFIGURATION) != 0) { changes &= ~CONFIG_WINDOW_CONFIGURATION; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index c6cece3d2b23..59d40cf36e9d 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2707,7 +2707,8 @@ class ActivityStarter { launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT; break; case ActivityInfo.DOCUMENT_LAUNCH_NEVER: - launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK; + launchFlags &= + ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK); break; } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 9f6087834a4e..c78f9ec21516 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5661,16 +5661,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } return false; /* continue */ } - if (taskId != INVALID_TASK_ID) { + if (taskId == INVALID_TASK_ID) { + if (!nextWindow.canReceiveKeys()) { + return false; /* continue */ + } + } else { Task task = nextWindow.getTask(); if (task == null || !task.isTaskId(taskId)) { return false; /* continue */ } } - if (!nextWindow.canReceiveKeys()) { - return false; /* continue */ - } - return true; /* stop */ + + return true; /* stop, match found */ } }); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 01f0359fa548..d929d50919c6 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -448,7 +448,8 @@ public class DisplayPolicy { final Looper looper = UiThread.getHandler().getLooper(); mHandler = new PolicyHandler(looper); - mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler, + // TODO(b/181821798) Migrate SystemGesturesPointerEventListener to use window context. + mSystemGestures = new SystemGesturesPointerEventListener(mUiContext, mHandler, new SystemGesturesPointerEventListener.Callbacks() { @Override public void onSwipeFromTop() { diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 35e54912b33e..45c4233b40aa 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -30,6 +30,7 @@ import static com.android.server.wm.InsetsSourceProviderProto.CONTROLLABLE; import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET; import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL; import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET; +import static com.android.server.wm.InsetsSourceProviderProto.FINISH_SEAMLESS_ROTATE_FRAME_NUMBER; import static com.android.server.wm.InsetsSourceProviderProto.FRAME; import static com.android.server.wm.InsetsSourceProviderProto.IME_OVERRIDDEN_FRAME; import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING; @@ -58,7 +59,6 @@ import com.android.server.wm.SurfaceAnimator.AnimationType; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; import java.io.PrintWriter; -import java.util.function.Consumer; /** * Controller for a specific inset source on the server. It's called provider as it provides the @@ -84,16 +84,6 @@ class InsetsSourceProvider { private final Rect mImeOverrideFrame = new Rect(); private boolean mIsLeashReadyForDispatching; - private final Consumer<Transaction> mSetLeashPositionConsumer = t -> { - if (mControl != null) { - final SurfaceControl leash = mControl.getLeash(); - if (leash != null) { - final Point position = mControl.getSurfacePosition(); - t.setPosition(leash, position.x, position.y); - } - } - }; - /** The visibility override from the current controlling window. */ private boolean mClientVisible; @@ -159,6 +149,7 @@ class InsetsSourceProvider { // TODO: Ideally, we should wait for the animation to finish so previous window can // animate-out as new one animates-in. mWin.cancelAnimation(); + mWin.mPendingPositionChanged = null; mWin.mProvidedInsetsSources.remove(mSource.getType()); } ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win); @@ -257,16 +248,31 @@ class InsetsSourceProvider { if (mControl != null) { final Point position = getWindowFrameSurfacePosition(); if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) { - if (mWin.getWindowFrames().didFrameSizeChange()) { - mWin.applyWithNextDraw(mSetLeashPositionConsumer); + if (!mWin.getWindowFrames().didFrameSizeChange()) { + updateLeashPosition(-1 /* frameNumber */); + } else if (mWin.mInRelayout) { + updateLeashPosition(mWin.getFrameNumber()); } else { - mSetLeashPositionConsumer.accept(mWin.getPendingTransaction()); + mWin.mPendingPositionChanged = this; } mStateController.notifyControlChanged(mControlTarget); } } } + void updateLeashPosition(long frameNumber) { + if (mControl == null) { + return; + } + final SurfaceControl leash = mControl.getLeash(); + if (leash != null) { + final Transaction t = mDisplayContent.getPendingTransaction(); + final Point position = mControl.getSurfacePosition(); + t.setPosition(leash, position.x, position.y); + deferTransactionUntil(t, leash, frameNumber); + } + } + private Point getWindowFrameSurfacePosition() { final Rect frame = mWin.getFrame(); final Point position = new Point(); @@ -274,6 +280,14 @@ class InsetsSourceProvider { return position; } + private void deferTransactionUntil(Transaction t, SurfaceControl leash, long frameNumber) { + if (frameNumber >= 0) { + final SurfaceControl barrier = mWin.getClientViewRootSurface(); + t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber); + t.deferTransactionUntil(leash, barrier, frameNumber); + } + } + /** * @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget) */ diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 914e45641b45..64ff1084a6b8 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -20,6 +20,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.graphics.Matrix.MSCALE_X; +import static android.graphics.Matrix.MSCALE_Y; +import static android.graphics.Matrix.MSKEW_X; +import static android.graphics.Matrix.MSKEW_Y; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; @@ -229,7 +233,8 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - public void setFinishTaskBounds(int taskId, Rect destinationBounds) { + public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop, + float[] float9) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "setFinishTaskBounds(%d): bounds=%s", taskId, destinationBounds); final long token = Binder.clearCallingIdentity(); @@ -239,6 +244,8 @@ public class RecentsAnimationController implements DeathRecipient { final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); if (taskAdapter.mTask.mTaskId == taskId) { taskAdapter.mFinishBounds.set(destinationBounds); + taskAdapter.mFinishWindowCrop.set(windowCrop); + taskAdapter.mFinishTransform = float9; break; } } @@ -1084,6 +1091,9 @@ public class RecentsAnimationController implements DeathRecipient { private final Rect mLocalBounds = new Rect(); // The bounds of the target when animation is finished private final Rect mFinishBounds = new Rect(); + // Bounds and transform for the final transaction. + private final Rect mFinishWindowCrop = new Rect(); + private float[] mFinishTransform; TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) { mTask = task; @@ -1120,13 +1130,31 @@ public class RecentsAnimationController implements DeathRecipient { void onCleanup() { if (!mFinishBounds.isEmpty()) { - // Apply any pending bounds changes - final SurfaceControl taskSurface = mTask.getSurfaceControl(); - mTask.getPendingTransaction() - .setPosition(taskSurface, mFinishBounds.left, mFinishBounds.top) - .setWindowCrop(taskSurface, mFinishBounds.width(), mFinishBounds.height()) + final SurfaceControl taskSurface = mTask.mSurfaceControl; + final Transaction pendingTransaction = mTask.getPendingTransaction(); + if (mFinishTransform != null) { + pendingTransaction + .setMatrix(taskSurface, + mFinishTransform[MSCALE_X], mFinishTransform[MSKEW_Y], + mFinishTransform[MSKEW_X], mFinishTransform[MSCALE_Y]); + } + float left = mFinishBounds.left; + float top = mFinishBounds.top; + if (!mFinishWindowCrop.isEmpty()) { + pendingTransaction.setWindowCrop(taskSurface, mFinishWindowCrop); + if (mFinishTransform != null) { + // adjust the position for insets. + left -= mFinishWindowCrop.left * mFinishTransform[MSCALE_X]; + top -= mFinishWindowCrop.top * mFinishTransform[MSCALE_Y]; + } + } + pendingTransaction + .setPosition(taskSurface, left, top) .apply(); mTask.mLastRecentsAnimationBounds.set(mFinishBounds); + // reset the variables + mFinishTransform = null; + mFinishWindowCrop.setEmpty(); mFinishBounds.setEmpty(); } else if (!mTask.isAttached()) { // Apply the task's pending transaction in case it is detached and its transaction diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java index 1e8b8a5bb576..4cc369f0a187 100644 --- a/services/core/java/com/android/server/wm/SeamlessRotator.java +++ b/services/core/java/com/android/server/wm/SeamlessRotator.java @@ -102,6 +102,10 @@ public class SeamlessRotator { * window in the new orientation. */ void finish(Transaction t, WindowContainer win) { + if (win.mSurfaceControl == null || !win.mSurfaceControl.isValid()) { + return; + } + mTransform.reset(); t.setMatrix(win.mSurfaceControl, mTransform, mFloat9); t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a4b4726fe070..ad0ce5bf4b28 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4911,9 +4911,15 @@ class Task extends WindowContainer<WindowContainer> { task.mLastNonFullscreenBounds = lastNonFullscreenBounds; task.setBounds(lastNonFullscreenBounds); task.mWindowLayoutAffinity = windowLayoutAffinity; + if (activities.size() > 0) { + // We need to add the task into hierarchy before adding child to it. + final DisplayContent dc = + taskSupervisor.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY); + dc.getDefaultTaskDisplayArea().addChild(task, POSITION_BOTTOM); - for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - task.addChild(activities.get(activityNdx)); + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + task.addChild(activities.get(activityNdx)); + } } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task); diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java index 855dd7e416b7..b8d2febd5dd4 100644 --- a/services/core/java/com/android/server/wm/TaskPersister.java +++ b/services/core/java/com/android/server/wm/TaskPersister.java @@ -328,10 +328,19 @@ public class TaskPersister implements PersisterQueue.Listener { // mWriteQueue.add(new TaskWriteQueueItem(task)); final int taskId = task.mTaskId; - if (mService.mRootWindowContainer.anyTaskForId(taskId, + final boolean persistedTask = task.hasActivity(); + if (persistedTask && mRecentTasks.getTask(taskId) != null) { + // The persisted task is added into hierarchy and will also be + // added to recent tasks later. So this task should not exist + // in recent tasks before it is added. + Slog.wtf(TAG, "Existing persisted task with taskId " + taskId + + " found"); + } else if (!persistedTask + && mService.mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS) != null) { // Should not happen. - Slog.wtf(TAG, "Existing task with taskId " + taskId + "found"); + Slog.wtf(TAG, "Existing task with taskId " + taskId + + " found"); } else if (userId != task.mUserId) { // Should not happen. Slog.wtf(TAG, "Task with userId " + task.mUserId + " found in " diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java index ffd6d21c1026..9245f8c3efe5 100644 --- a/services/core/java/com/android/server/wm/WindowFrames.java +++ b/services/core/java/com/android/server/wm/WindowFrames.java @@ -113,7 +113,7 @@ public class WindowFrames { } /** - * @return true if the width or height has changed since last updating resizing window. + * @return true if the width or height has changed since last reported to the client. */ boolean didFrameSizeChange() { return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height()); @@ -135,13 +135,6 @@ public class WindowFrames { } /** - * @return true if the width or height has changed since last reported to the client. - */ - boolean isFrameSizeChangeReported() { - return mFrameSizeChanged || didFrameSizeChange(); - } - - /** * Resets the size changed flags so they're all set to false again. This should be called * after the frames are reported to client. */ diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 53ebfb2c6e0e..8148f15981b3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -451,24 +451,15 @@ public abstract class WindowManagerInternal { public abstract int getInputMethodWindowVisibleHeight(int displayId); /** - * Notifies WindowManagerService that the current IME window status is being changed. + * Notifies WindowManagerService that the expected back-button behavior might have changed. * * <p>Only {@link com.android.server.inputmethod.InputMethodManagerService} is the expected and * tested caller of this method.</p> * - * @param imeToken token to track the active input method. Corresponding IME windows can be - * identified by checking {@link android.view.WindowManager.LayoutParams#token}. - * Note that there is no guarantee that the corresponding window is already - * created - * @param imeWindowVisible whether the active IME thinks that its window should be visible or - * hidden, no matter how WindowManagerService will react / has reacted - * to corresponding API calls. Note that this state is not guaranteed - * to be synchronized with state in WindowManagerService. * @param dismissImeOnBackKeyPressed {@code true} if the software keyboard is shown and the back * key is expected to dismiss the software keyboard. */ - public abstract void updateInputMethodWindowStatus(@NonNull IBinder imeToken, - boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed); + public abstract void setDismissImeOnBackKeyPressed(boolean dismissImeOnBackKeyPressed); /** * Notifies WindowManagerService that the current IME window status is being changed. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f0dc7fe08c66..dce79a093a6f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -231,7 +231,7 @@ import android.view.IOnKeyguardExitResult; import android.view.IPinnedTaskListener; import android.view.IRecentsAnimationRunner; import android.view.IRotationWatcher; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.ISystemGestureExclusionListener; import android.view.IWallpaperVisibilityListener; import android.view.IWindow; @@ -2232,6 +2232,11 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayContent dc = win.getDisplayContent(); + if (win.mPendingPositionChanged != null) { + win.mPendingPositionChanged.updateLeashPosition(frameNumber); + win.mPendingPositionChanged = null; + } + if (mUseBLASTSync && win.useBLASTSync() && viewVisibility != View.GONE) { win.prepareDrawHandlers(); result |= RELAYOUT_RES_BLAST_SYNC; @@ -7140,10 +7145,10 @@ public class WindowManagerService extends IWindowManager.Stub * @param displayId the display for the request * @param behindClient token for a window, used to filter the search to windows behind it * @param taskId specifies the id of a task the result must belong to or -1 to match any task - * @param callbacks to receive responses + * @param listener to receive the response */ public void requestScrollCapture(int displayId, @Nullable IBinder behindClient, int taskId, - IScrollCaptureCallbacks callbacks) { + IScrollCaptureResponseListener listener) { if (!checkCallingPermission(READ_FRAME_BUFFER, "requestScrollCapture()")) { throw new SecurityException("Requires READ_FRAME_BUFFER permission"); } @@ -7156,7 +7161,7 @@ public class WindowManagerService extends IWindowManager.Stub ProtoLog.e(WM_ERROR, "Invalid displayId for requestScrollCapture: %d", displayId); responseBuilder.setDescription(String.format("bad displayId: %d", displayId)); - callbacks.onScrollCaptureResponse(responseBuilder.build()); + listener.onScrollCaptureResponse(responseBuilder.build()); return; } WindowState topWindow = null; @@ -7166,19 +7171,19 @@ public class WindowManagerService extends IWindowManager.Stub WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId); if (targetWindow == null) { responseBuilder.setDescription("findScrollCaptureTargetWindow returned null"); - callbacks.onScrollCaptureResponse(responseBuilder.build()); + listener.onScrollCaptureResponse(responseBuilder.build()); return; } try { // Forward to the window for handling, which will respond using the callback. - targetWindow.mClient.requestScrollCapture(callbacks); + targetWindow.mClient.requestScrollCapture(listener); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "requestScrollCapture: caught exception dispatching to window." + "token=%s", targetWindow.mClient.asBinder()); responseBuilder.setWindowTitle(targetWindow.getName()); responseBuilder.setDescription(String.format("caught exception: %s", e)); - callbacks.onScrollCaptureResponse(responseBuilder.build()); + listener.onScrollCaptureResponse(responseBuilder.build()); } } } catch (RemoteException e) { @@ -7713,8 +7718,7 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void updateInputMethodWindowStatus(@NonNull IBinder imeToken, - boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) { + public void setDismissImeOnBackKeyPressed(boolean dismissImeOnBackKeyPressed) { mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 7ebc1cc6d5c1..2eadcd581417 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -726,6 +726,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private InsetsState mFrozenInsetsState; + @Nullable InsetsSourceProvider mPendingPositionChanged; + private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; private KeyInterceptionInfo mKeyInterceptionInfo; @@ -772,12 +774,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP updateSurfacePosition(t); }; - private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> { - if (mSurfaceControl != null && mSurfaceControl.isValid()) { - t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); - } - }; - /** * @see #setSurfaceTranslationY(int) */ @@ -2133,8 +2129,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP : getTask().getWindowConfiguration().hasMovementAnimations(); if (mToken.okToAnimate() && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0 - && !mWindowFrames.didFrameSizeChange() - && !surfaceInsetsChanging() && !isDragResizing() && hasMovementAnimation && !mWinAnimator.mLastHidden @@ -5324,17 +5318,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // prior to the rotation. if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null && !mLastSurfacePosition.equals(mSurfacePosition)) { - final boolean frameSizeChanged = mWindowFrames.isFrameSizeChangeReported(); - final boolean surfaceInsetsChanged = surfaceInsetsChanging(); - final boolean surfaceSizeChanged = frameSizeChanged || surfaceInsetsChanged; + t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y); - if (surfaceInsetsChanged) { + if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) { mLastSurfaceInsets.set(mAttrs.surfaceInsets); - } - if (surfaceSizeChanged) { - applyWithNextDraw(mSetSurfacePositionConsumer); - } else { - mSetSurfacePositionConsumer.accept(t); + t.deferTransactionUntil(mSurfaceControl, + mWinAnimator.mSurfaceController.mSurfaceControl, + getFrameNumber()); } } } diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp index a73f6c6d8c2d..6cb4a63a5636 100644 --- a/services/core/jni/com_android_server_SystemServer.cpp +++ b/services/core/jni/com_android_server_SystemServer.cpp @@ -123,16 +123,9 @@ static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* } static void android_server_SystemServer_fdtrackAbort(JNIEnv*, jobject) { - raise(BIONIC_SIGNAL_FDTRACK); - - // Wait for a bit to allow fdtrack to dump backtraces to logcat. - std::this_thread::sleep_for(5s); - - // Abort on a different thread to avoid ART dumping runtime stacks. - std::thread([]() { - LOG_ALWAYS_FATAL("b/140703823: aborting due to fd leak: check logs for fd " - "backtraces"); - }).join(); + sigval val; + val.sival_int = 1; + sigqueue(getpid(), BIONIC_SIGNAL_FDTRACK, val); } static jlong android_server_SystemServer_startIncrementalService(JNIEnv* env, jclass klass, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java index e0c5e328f8c7..8027e5b9d9bc 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java @@ -79,16 +79,16 @@ public class CertificateMonitor { X509Certificate cert = parseCert(certBuffer); pemCert = Credentials.convertToPem(cert); } catch (CertificateException | IOException ce) { - Slog.e(LOG_TAG, ce, "Problem converting cert"); + Slog.e(LOG_TAG, "Problem converting cert", ce); return null; } try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) { return keyChainConnection.getService().installCaCertificate(pemCert); } catch (RemoteException e) { - Slog.e(LOG_TAG, e, "installCaCertsToKeyChain(): "); + Slog.e(LOG_TAG, "installCaCertsToKeyChain(): ", e); } catch (InterruptedException e1) { - Slog.w(LOG_TAG, e1, "installCaCertsToKeyChain(): "); + Slog.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1); Thread.currentThread().interrupt(); } return null; @@ -100,9 +100,9 @@ public class CertificateMonitor { keyChainConnection.getService().deleteCaCertificate(aliases[i]); } } catch (RemoteException e) { - Slog.e(LOG_TAG, e, "from CaCertUninstaller: "); + Slog.e(LOG_TAG, "from CaCertUninstaller: ", e); } catch (InterruptedException ie) { - Slog.w(LOG_TAG, ie, "CaCertUninstaller: "); + Slog.w(LOG_TAG, "CaCertUninstaller: ", ie); Thread.currentThread().interrupt(); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1a2eee06da4f..70756787c561 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1056,7 +1056,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { service.removeCredentialManagementApp(); } } catch (RemoteException | InterruptedException | IllegalStateException e) { - Log.e(LOG_TAG, "Unable to remove the credential management app"); + Slog.e(LOG_TAG, "Unable to remove the credential management app"); } }); } @@ -1149,18 +1149,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @OperationSafetyReason int reason) { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); - Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %s)", + Slog.i(LOG_TAG, "setNextOperationSafety(%s, %s)", DevicePolicyManager.operationToString(operation), - DevicePolicyManager.operationSafetyReasonToString(reason))); + DevicePolicyManager.operationSafetyReasonToString(reason)); mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason); } @Override public boolean isSafeOperation(@OperationSafetyReason int reason) { if (VERBOSE_LOG) { - Slog.v(LOG_TAG, "checking isSafeOperation(" - + DevicePolicyManager.operationSafetyReasonToString(reason) - + ") using mSafetyChecker " + mSafetyChecker); + Slog.v(LOG_TAG, "checking isSafeOperation(%s) using mSafetyChecker %s", + DevicePolicyManager.operationSafetyReasonToString(reason), mSafetyChecker); } return mSafetyChecker == null ? true : mSafetyChecker.isSafeOperation(reason); } @@ -1893,9 +1892,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } - Slog.i(LOG_TAG, String.format( - "Migrating COMP to PO on a corp owned device; primary user: %d; profile: %d", - doUserId, poUserId)); + Slog.i(LOG_TAG, "Migrating COMP to PO on a corp owned device; primary user: %d; " + + "profile: %d", doUserId, poUserId); Slog.i(LOG_TAG, "Giving the PO additional power..."); markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId); @@ -1940,11 +1938,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void uninstallOrDisablePackage(String packageName, int userHandle) { + private void uninstallOrDisablePackage(String packageName, @UserIdInt int userId) { final ApplicationInfo appInfo; try { appInfo = mIPackageManager.getApplicationInfo( - packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userHandle); + packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId); } catch (RemoteException e) { // Shouldn't happen. return; @@ -1954,10 +1952,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - Slog.i(LOG_TAG, String.format( - "Package %s is pre-installed, marking disabled until used", packageName)); + Slog.i(LOG_TAG, "Package %s is pre-installed, marking disabled until used", + packageName); mContext.getPackageManager().setApplicationEnabledSetting(packageName, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0 /* flags */); + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0); return; } @@ -1968,17 +1966,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int status = intent.getIntExtra( PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); if (status == PackageInstaller.STATUS_SUCCESS) { - Slog.i(LOG_TAG, String.format( - "Package %s uninstalled for user %d", packageName, userHandle)); + Slog.i(LOG_TAG, "Package %s uninstalled for user %d", packageName, userId); } else { - Slog.e(LOG_TAG, String.format( - "Failed to uninstall %s; status: %d", packageName, status)); + Slog.e(LOG_TAG, "Failed to uninstall %s; status: %d", packageName, status); } } }; - final PackageInstaller pi = mInjector.getPackageManager(userHandle).getPackageInstaller(); - pi.uninstall(packageName, 0 /* flags */, new IntentSender((IIntentSender) mLocalSender)); + final PackageInstaller pi = mInjector.getPackageManager(userId).getPackageInstaller(); + pi.uninstall(packageName, /* flags= */ 0, new IntentSender((IIntentSender) mLocalSender)); } @GuardedBy("getLockObject()") @@ -2177,7 +2173,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { !mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()); mOwners.writeDeviceOwner(); if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Device owner component filled in"); + Slog.v(LOG_TAG, "Device owner component filled in"); } } } @@ -2192,7 +2188,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // except for the "system controlled" ones. if (mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()) { if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Migrating DO user restrictions"); + Slog.v(LOG_TAG, "Migrating DO user restrictions"); } migrated = true; @@ -2220,7 +2216,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = ui.id; if (mOwners.getProfileOwnerUserRestrictionsNeedsMigration(userId)) { if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Migrating PO user restrictions for user " + userId); + Slog.v(LOG_TAG, "Migrating PO user restrictions for user %d", userId); } migrated = true; @@ -2243,7 +2239,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } if (VERBOSE_LOG && migrated) { - Log.v(LOG_TAG, "User restrictions migrated."); + Slog.v(LOG_TAG, "User restrictions migrated."); } } @@ -2271,9 +2267,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (VERBOSE_LOG) { - Log.v(LOG_TAG, "origRestrictions=" + origRestrictions); - Log.v(LOG_TAG, "newBaseRestrictions=" + newBaseRestrictions); - Log.v(LOG_TAG, "newOwnerRestrictions=" + newOwnerRestrictions); + Slog.v(LOG_TAG, "origRestrictions=%s", origRestrictions); + Slog.v(LOG_TAG, "newBaseRestrictions=%s", newBaseRestrictions); + Slog.v(LOG_TAG, "newOwnerRestrictions=%s", newOwnerRestrictions); } mUserManagerInternal.setBaseUserRestrictionsByDpmsForMigration(user.getIdentifier(), newBaseRestrictions); @@ -2768,9 +2764,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private JournaledFile makeJournaledFile(@UserIdInt int userId, String fileName) { final String base = new File(getPolicyFileDirectory(userId), fileName) .getAbsolutePath(); - if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Opening " + base); - } + if (VERBOSE_LOG) Slog.v(LOG_TAG, "Opening %s", base); return new JournaledFile(new File(base), new File(base + ".tmp")); } @@ -4920,8 +4914,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (!validationErrors.isEmpty()) { - Log.w(LOG_TAG, "Failed to reset password due to constraint violation: " - + validationErrors.get(0)); + Slog.w(LOG_TAG, "Failed to reset password due to constraint violation: %s", + validationErrors.get(0)); return false; } } @@ -5349,7 +5343,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { }); if (alias == null) { - Log.w(LOG_TAG, "Problem installing cert"); + Slog.w(LOG_TAG, "Problem installing cert"); return false; } @@ -5422,12 +5416,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); return true; } catch (RemoteException e) { - Log.e(LOG_TAG, "Installing certificate", e); + Slog.e(LOG_TAG, "Installing certificate", e); } finally { keyChainConnection.close(); } } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while installing certificate", e); + Slog.w(LOG_TAG, "Interrupted while installing certificate", e); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); @@ -5472,12 +5466,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); return keyChain.removeKeyPair(alias); } catch (RemoteException e) { - Log.e(LOG_TAG, "Removing keypair", e); + Slog.e(LOG_TAG, "Removing keypair", e); } finally { keyChainConnection.close(); } } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while removing keypair", e); + Slog.w(LOG_TAG, "Interrupted while removing keypair", e); Thread.currentThread().interrupt(); } finally { Binder.restoreCallingIdentity(id); @@ -5495,9 +5489,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { KeyChain.bindAsUser(mContext, caller.getUserHandle())) { return keyChainConnection.getService().containsKeyPair(alias); } catch (RemoteException e) { - Log.e(LOG_TAG, "Querying keypair", e); + Slog.e(LOG_TAG, "Querying keypair", e); } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while querying keypair", e); + Slog.w(LOG_TAG, "Interrupted while querying keypair", e); Thread.currentThread().interrupt(); } return false; @@ -5539,7 +5533,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } return false; } catch (RemoteException e) { - Log.e(LOG_TAG, "Querying grant to wifi auth. ", e); + Slog.e(LOG_TAG, "Querying grant to wifi auth.", e); return false; } }); @@ -5580,11 +5574,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { keyChain.setGrant(granteeUid, alias, hasGrant); return true; } catch (RemoteException e) { - Log.e(LOG_TAG, "Setting grant for package.", e); + Slog.e(LOG_TAG, "Setting grant for package.", e); return false; } } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while setting key grant", e); + Slog.w(LOG_TAG, "Interrupted while setting key grant", e); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); @@ -5621,9 +5615,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } return result; } catch (RemoteException e) { - Log.e(LOG_TAG, "Querying keypair grants", e); + Slog.e(LOG_TAG, "Querying keypair grants", e); } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while querying keypair grants", e); + Slog.w(LOG_TAG, "Interrupted while querying keypair grants", e); Thread.currentThread().interrupt(); } return Collections.emptyList(); @@ -5748,7 +5742,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // As the caller will be granted access to the key, ensure no UID was specified, as // it will not have the desired effect. if (keySpec.getUid() != KeyStore.UID_SELF) { - Log.e(LOG_TAG, "Only the caller can be granted access to the generated keypair."); + Slog.e(LOG_TAG, "Only the caller can be granted access to the generated keypair."); logGenerateKeyPairFailure(caller, isCredentialManagementApp); return false; } @@ -5774,8 +5768,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int generationResult = keyChain.generateKeyPair(algorithm, new ParcelableKeyGenParameterSpec(keySpec)); if (generationResult != KeyChain.KEY_GEN_SUCCESS) { - Log.e(LOG_TAG, String.format( - "KeyChain failed to generate a keypair, error %d.", generationResult)); + Slog.e(LOG_TAG, "KeyChain failed to generate a keypair, error %d.", + generationResult); logGenerateKeyPairFailure(caller, isCredentialManagementApp); switch (generationResult) { case KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE: @@ -5814,7 +5808,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts)); } catch (CertificateException e) { logGenerateKeyPairFailure(caller, isCredentialManagementApp); - Log.e(LOG_TAG, "While retrieving certificate chain.", e); + Slog.e(LOG_TAG, "While retrieving certificate chain.", e); return false; } @@ -5829,9 +5823,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } } catch (RemoteException e) { - Log.e(LOG_TAG, "KeyChain error while generating a keypair", e); + Slog.e(LOG_TAG, "KeyChain error while generating a keypair", e); } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while generating keypair", e); + Slog.w(LOG_TAG, "Interrupted while generating keypair", e); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); @@ -5889,10 +5883,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); return true; } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while setting keypair certificate", e); + Slog.w(LOG_TAG, "Interrupted while setting keypair certificate", e); Thread.currentThread().interrupt(); } catch (RemoteException e) { - Log.e(LOG_TAG, "Failed setting keypair certificate", e); + Slog.e(LOG_TAG, "Failed setting keypair certificate", e); } finally { mInjector.binderRestoreCallingIdentity(id); } @@ -5966,7 +5960,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } catch (Exception e) { // Caller could throw RuntimeException or RemoteException back across processes. Catch // everything just to be sure. - Log.e(LOG_TAG, "error while responding to callback", e); + Slog.e(LOG_TAG, "error while responding to callback", e); } } @@ -6323,7 +6317,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean isUserSelectable) { // Should not be user selectable if (isUserSelectable) { - Log.e(LOG_TAG, "The credential management app is not allowed to install a " + Slog.e(LOG_TAG, "The credential management app is not allowed to install a " + "user selectable key pair"); return false; } @@ -6523,8 +6517,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Persist the request so the device is automatically factory-reset on next start if // the system crashes or reboots before the {@code DevicePolicySafetyChecker} calls // its callback. - Slog.i(LOG_TAG, String.format("Persisting factory reset request as it could be " - + "delayed by %s", mSafetyChecker)); + Slog.i(LOG_TAG, "Persisting factory reset request as it could be delayed by %s", + mSafetyChecker); synchronized (getLockObject()) { DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); policy.setDelayedFactoryReset(reason, wipeExtRequested, wipeEuicc, @@ -6638,8 +6632,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { int userId = admin != null ? admin.getUserHandle().getIdentifier() : caller.getUserId(); - Slog.i(LOG_TAG, String.format("wipeDataWithReason(%s): admin=%s, user=%d", - wipeReasonForUser, admin, userId)); + Slog.i(LOG_TAG, "wipeDataWithReason(%s): admin=%s, user=%d", wipeReasonForUser, admin, + userId); if (calledByProfileOwnerOnOrgOwnedDevice) { // When wipeData is called on the parent instance, it implies wiping the entire device. if (calledOnParentInstance) { @@ -7439,7 +7433,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { mInjector.getIWindowManager().refreshScreenCaptureDisabled(userHandle); } catch (RemoteException e) { - Log.w(LOG_TAG, "Unable to notify WindowManager.", e); + Slog.w(LOG_TAG, "Unable to notify WindowManager.", e); } }); } @@ -8817,6 +8811,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } final ComponentName supervisorComponent = ComponentName.unflattenFromString(supervisor); + if (supervisorComponent == null) { + return null; + } if (supervisorComponent.equals(doComponent) || supervisorComponent.equals( poComponent)) { return supervisorComponent; @@ -8903,19 +8900,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // thrown but null data can be returned; if the appInfo for the specified package cannot // be found then return false to prevent crashing the app. if (appInfo == null) { - Log.w(LOG_TAG, - String.format("appInfo could not be found for package %s", packageName)); + Slog.w(LOG_TAG, "appInfo could not be found for package %s", packageName); return false; } else if (uid != appInfo.uid) { String message = String.format("Package %s (uid=%d) does not match provided uid %d", packageName, appInfo.uid, uid); - Log.w(LOG_TAG, message); + Slog.w(LOG_TAG, message); throw new SecurityException(message); } } catch (RemoteException e) { // If an exception is caught obtaining the appInfo just return false to prevent crashing // apps due to an internal error. - Log.e(LOG_TAG, "Exception caught obtaining appInfo for package " + packageName, e); + Slog.e(LOG_TAG, e, "Exception caught obtaining appInfo for package %s", packageName); return false; } return true; @@ -8931,7 +8927,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String message = String.format( "Calling uid %d, pid %d cannot check device identifier access for package %s " + "(uid=%d, pid=%d)", callingUid, callingPid, packageName, uid, pid); - Log.w(LOG_TAG, message); + Slog.w(LOG_TAG, message); throw new SecurityException(message); } } @@ -8939,14 +8935,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** * Canonical name for a given package. */ - private String getApplicationLabel(String packageName, int userHandle) { + private String getApplicationLabel(String packageName, @UserIdInt int userId) { return mInjector.binderWithCleanCallingIdentity(() -> { final Context userContext; try { - UserHandle handle = new UserHandle(userHandle); - userContext = mContext.createPackageContextAsUser(packageName, 0, handle); + UserHandle userHandle = UserHandle.of(userId); + userContext = mContext.createPackageContextAsUser(packageName, /* flags= */ 0, + userHandle); } catch (PackageManager.NameNotFoundException nnfe) { - Log.w(LOG_TAG, packageName + " is not installed for user " + userHandle, nnfe); + Slog.w(LOG_TAG, nnfe, "%s is not installed for user %d", packageName, userId); return null; } ApplicationInfo appInfo = userContext.getApplicationInfo(); @@ -9565,9 +9562,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } result.add(info.options); } else { - Log.w(LOG_TAG, "Ignoring admin " + active.info - + " because it has trust options but doesn't declare " - + "KEYGUARD_DISABLE_TRUST_AGENTS"); + Slog.w(LOG_TAG, "Ignoring admin %s because it has trust options but doesn't" + + " declare KEYGUARD_DISABLE_TRUST_AGENTS", active.info); } } else if (disablesTrust) { allAdminsHaveOptions = false; @@ -9705,7 +9701,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { userIdToCheck); systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } catch (RemoteException e) { - Log.i(LOG_TAG, "Can't talk to package managed", e); + Slog.i(LOG_TAG, "Can't talk to package managed", e); } if (!systemService && !permittedList.contains(enabledPackage)) { return false; @@ -10186,7 +10182,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { user = userInfo.getUserHandle(); } } catch (UserManager.CheckedUserOperationException e) { - Log.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e); + Slog.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e); } } finally { mInjector.binderRestoreCallingIdentity(id); @@ -10257,8 +10253,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } catch (RemoteException e) { // Does not happen, same process - Slog.wtf(LOG_TAG, String.format("Failed to install admin package %s for user %d", - adminPkg, userId), e); + Slog.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d", + adminPkg, userId); } // Set admin. @@ -10307,7 +10303,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin, /* managedUser= */ userId, /* adminExtras= */ null, /* showDisclaimer= */ true); } else { - Log.i(LOG_TAG, "User " + userId + " added on DO mode; setting ShowNewUserDisclaimer"); + Slog.i(LOG_TAG, "User %d added on DO mode; setting ShowNewUserDisclaimer", userId); setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED); } } @@ -10363,8 +10359,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER; if (isAdminAffectedByRestriction(who, restriction, caller.getUserId())) { - Log.w(LOG_TAG, "The device owner cannot remove a user because " - + restriction + " is enabled, and was not set by the device owner"); + Slog.w(LOG_TAG, "The device owner cannot remove a user because %s is enabled, and " + + "was not set by the device owner", restriction); return false; } return mUserManagerInternal.removeUserEvenWhenDisallowed(userHandle.getIdentifier()); @@ -10401,7 +10397,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } return mInjector.getIActivityManager().switchUser(userId); } catch (RemoteException e) { - Log.e(LOG_TAG, "Couldn't switch user", e); + Slog.e(LOG_TAG, "Couldn't switch user", e); return false; } finally { mInjector.binderRestoreCallingIdentity(id); @@ -10419,19 +10415,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = userHandle.getIdentifier(); if (isManagedProfile(userId)) { - Log.w(LOG_TAG, "Managed profile cannot be started in background"); + Slog.w(LOG_TAG, "Managed profile cannot be started in background"); return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE; } final long id = mInjector.binderClearCallingIdentity(); try { if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) { - Log.w(LOG_TAG, "Cannot start user " + userId + ", too many users in background"); + Slog.w(LOG_TAG, "Cannot start user %d, too many users in background", userId); return UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS; } if (mInjector.getIActivityManager().startUserInBackground(userId)) { - Log.i(LOG_TAG, "Started used " + userId + " in background"); + Slog.i(LOG_TAG, "Started used %d in background", userId); return UserManager.USER_OPERATION_SUCCESS; } else { return UserManager.USER_OPERATION_ERROR_UNKNOWN; @@ -10454,7 +10450,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = userHandle.getIdentifier(); if (isManagedProfile(userId)) { - Log.w(LOG_TAG, "Managed profile cannot be stopped"); + Slog.w(LOG_TAG, "Managed profile cannot be stopped"); return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE; } @@ -10477,14 +10473,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (isManagedProfile(callingUserId)) { - Log.w(LOG_TAG, "Managed profile cannot be logout"); + Slog.w(LOG_TAG, "Managed profile cannot be logout"); return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE; } final long id = mInjector.binderClearCallingIdentity(); try { if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) { - Log.w(LOG_TAG, "Failed to switch to primary user"); + Slog.w(LOG_TAG, "Failed to switch to primary user"); // This should never happen as target user is UserHandle.USER_SYSTEM return UserManager.USER_OPERATION_ERROR_UNKNOWN; } @@ -11261,8 +11257,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (isCrossProfileQuickContactDisabled(managedUserId)) { if (VERBOSE_LOG) { - Log.v(LOG_TAG, - "Cross-profile contacts access disabled for user " + managedUserId); + Slog.v(LOG_TAG, "Cross-profile contacts access disabled for user %d", + managedUserId); } return; } @@ -11275,7 +11271,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** * @return true if cross-profile QuickContact is disabled */ - private boolean isCrossProfileQuickContactDisabled(int userId) { + private boolean isCrossProfileQuickContactDisabled(@UserIdInt int userId) { return getCrossProfileCallerIdDisabledForUser(userId) && getCrossProfileContactsSearchDisabledForUser(userId); } @@ -11284,23 +11280,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * @return the user ID of the managed user that is linked to the current user, if any. * Otherwise -1. */ - public int getManagedUserId(int callingUserId) { - if (VERBOSE_LOG) { - Log.v(LOG_TAG, "getManagedUserId: callingUserId=" + callingUserId); - } + public int getManagedUserId(@UserIdInt int callingUserId) { + if (VERBOSE_LOG) Slog.v(LOG_TAG, "getManagedUserId: callingUserId=%d", callingUserId); for (UserInfo ui : mUserManager.getProfiles(callingUserId)) { if (ui.id == callingUserId || !ui.isManagedProfile()) { continue; // Caller user self, or not a managed profile. Skip. } - if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Managed user=" + ui.id); - } + if (VERBOSE_LOG) Slog.v(LOG_TAG, "Managed user=%d", ui.id); return ui.id; } - if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Managed user not found."); - } + if (VERBOSE_LOG) Slog.v(LOG_TAG, "Managed user not found."); return -1; } @@ -11599,7 +11589,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Some settings are no supported any more. However we do not want to throw a // SecurityException to avoid breaking apps. if (GLOBAL_SETTINGS_DEPRECATED.contains(setting)) { - Log.i(LOG_TAG, "Global setting no longer supported: " + setting); + Slog.i(LOG_TAG, "Global setting no longer supported: %s", setting); return; } @@ -12335,7 +12325,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING, UserHandle.of(userId))) { - Log.e(LOG_TAG, "printing is enabled"); + Slog.e(LOG_TAG, "printing is enabled for user %d", userId); return null; } String ownerPackage = mOwners.getProfileOwnerPackage(userId); @@ -12348,22 +12338,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { return pm.getPackageInfo(packageName, 0); } catch (NameNotFoundException e) { - Log.e(LOG_TAG, "getPackageInfo error", e); + Slog.e(LOG_TAG, "getPackageInfo error", e); return null; } }); if (packageInfo == null) { - Log.e(LOG_TAG, "packageInfo is inexplicably null"); + Slog.e(LOG_TAG, "packageInfo is inexplicably null"); return null; } ApplicationInfo appInfo = packageInfo.applicationInfo; if (appInfo == null) { - Log.e(LOG_TAG, "appInfo is inexplicably null"); + Slog.e(LOG_TAG, "appInfo is inexplicably null"); return null; } CharSequence appLabel = pm.getApplicationLabel(appInfo); if (appLabel == null) { - Log.e(LOG_TAG, "appLabel is inexplicably null"); + Slog.e(LOG_TAG, "appLabel is inexplicably null"); return null; } return ((Context) ActivityThread.currentActivityThread().getSystemUiContext()) @@ -12417,9 +12407,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(intent); Objects.requireNonNull(parentHandle); final int userId = parentHandle.getIdentifier(); - Slog.i(LOG_TAG, - String.format("Sending %s broadcast to manifest receivers.", - intent.getAction())); + Slog.i(LOG_TAG, "Sending %s broadcast to manifest receivers.", intent.getAction()); try { final List<ResolveInfo> receivers = mIPackageManager.queryIntentReceivers( intent, /* resolvedType= */ null, @@ -12429,9 +12417,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (checkCrossProfilePackagePermissions(packageName, userId, requiresPermission) || checkModifyQuietModePermission(packageName, userId)) { - Slog.i(LOG_TAG, - String.format("Sending %s broadcast to %s.", intent.getAction(), - packageName)); + Slog.i(LOG_TAG, "Sending %s broadcast to %s.", intent.getAction(), + packageName); final Intent packageIntent = new Intent(intent) .setComponent(receiver.getComponentInfo().getComponentName()) .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); @@ -12439,9 +12426,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } } catch (RemoteException ex) { - Slog.w(LOG_TAG, - String.format("Cannot get list of broadcast receivers for %s because: %s.", - intent.getAction(), ex)); + Slog.w(LOG_TAG, "Cannot get list of broadcast receivers for %s because: %s.", + intent.getAction(), ex); } } @@ -12459,9 +12445,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { android.Manifest.permission.MODIFY_QUIET_MODE, uid, /* owningUid= */ -1, /* exported= */ true); } catch (NameNotFoundException ex) { - Slog.w(LOG_TAG, - String.format("Cannot find the package %s to check for permissions.", - packageName)); + Slog.w(LOG_TAG, "Cannot find the package %s to check for permissions.", + packageName); return false; } } @@ -12490,9 +12475,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return crossProfileAppsService.verifyPackageHasInteractAcrossProfilePermission( packageName, userId); } catch (NameNotFoundException ex) { - Slog.w(LOG_TAG, - String.format("Cannot find the package %s to check for permissions.", - packageName)); + Slog.w(LOG_TAG, "Cannot find the package %s to check for permissions.", + packageName); return false; } } @@ -12566,8 +12550,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // TODO(b/178494483): use EventLog instead // TODO(b/178494483): log metrics? if (VERBOSE_LOG) { - Slog.v(LOG_TAG, String.format("notifyUnsafeOperationStateChanged(): %s=%b", - DevicePolicyManager.operationSafetyReasonToString(reason), isSafe)); + Slog.v(LOG_TAG, "notifyUnsafeOperationStateChanged(): %s=%b", + DevicePolicyManager.operationSafetyReasonToString(reason), isSafe); } Preconditions.checkArgument(mSafetyChecker == checker, "invalid checker: should be %s, was %s", mSafetyChecker, checker); @@ -12849,7 +12833,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { runningUserIds = mInjector.getIActivityManager().getRunningUserIds(); } catch (RemoteException e) { // Shouldn't happen. - Log.e(LOG_TAG, "Could not retrieve the list of running users", e); + Slog.e(LOG_TAG, "Could not retrieve the list of running users", e); return; } // Send broadcasts to corresponding profile owners if any. @@ -13210,9 +13194,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode() ? UserHandle.USER_SYSTEM : callingUserId; - Slog.i(LOG_TAG, - String.format("Calling user %d, device owner will be set on user %d", - callingUserId, deviceOwnerUserId)); + Slog.i(LOG_TAG, "Calling user %d, device owner will be set on user %d", + callingUserId, deviceOwnerUserId); // hasIncompatibleAccountsOrNonAdb doesn't matter since the caller is not adb. return checkDeviceOwnerProvisioningPreConditionLocked(/* owner unknown */ null, deviceOwnerUserId, callingUserId, /* isAdb= */ false, @@ -13242,9 +13225,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle); if (mUserManager.getUserInfo(callingUserId).isProfile()) { - Slog.i(LOG_TAG, - String.format("Calling user %d is a profile, cannot add another.", - callingUserId)); + Slog.i(LOG_TAG, "Calling user %d is a profile, cannot add another.", callingUserId); // The check is called from inside a managed profile. A managed profile cannot // be provisioned from within another managed profile. return CODE_CANNOT_ADD_MANAGED_PROFILE; @@ -13257,16 +13238,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Do not allow adding a managed profile if there's a restriction. if (addingProfileRestricted) { - Slog.i(LOG_TAG, String.format( - "Adding a profile is restricted: User %s Has device owner? %b", - callingUserHandle, hasDeviceOwner)); + Slog.i(LOG_TAG, "Adding a profile is restricted: User %s Has device owner? %b", + callingUserHandle, hasDeviceOwner); return CODE_CANNOT_ADD_MANAGED_PROFILE; } // Bail out if we are trying to provision a work profile but one already exists. if (!mUserManager.canAddMoreManagedProfiles( callingUserId, /* allowedToRemoveOne= */ false)) { - Slog.i(LOG_TAG, String.format("A work profile already exists.")); + Slog.i(LOG_TAG, "A work profile already exists."); return CODE_CANNOT_ADD_MANAGED_PROFILE; } } finally { @@ -13754,9 +13734,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { who.flattenToString(), userId)); } - Slog.i(LOG_TAG, String.format( - "Marking %s as profile owner on organization-owned device for user %d", - who.flattenToString(), userId)); + Slog.i(LOG_TAG, "Marking %s as profile owner on organization-owned device for user %d", + who.flattenToString(), userId); // First, set restriction on removing the profile. mInjector.binderWithCleanCallingIdentity(() -> { @@ -14169,7 +14148,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { // force stop the package before uninstalling mInjector.getIActivityManager().forceStopPackage(packageName, userId); } catch (RemoteException re) { - Log.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package"); + Slog.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package"); } final Uri packageURI = Uri.parse("package:" + packageName); final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); @@ -14425,7 +14404,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } synchronized (getLockObject()) { if (owner == null || !isAdminTestOnlyLocked(owner, userId)) { - Log.w(LOG_TAG, + Slog.w(LOG_TAG, "Non test-only owner can't be installed with existing accounts."); return true; } @@ -14439,20 +14418,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean compatible = true; for (Account account : accounts) { if (hasAccountFeatures(am, account, feature_disallow)) { - Log.e(LOG_TAG, account + " has " + feature_disallow[0]); + Slog.e(LOG_TAG, "%s has %s", account, feature_disallow[0]); compatible = false; break; } if (!hasAccountFeatures(am, account, feature_allow)) { - Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]); + Slog.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]); compatible = false; break; } } if (compatible) { - Log.w(LOG_TAG, "All accounts are compatible"); + Slog.w(LOG_TAG, "All accounts are compatible"); } else { - Log.e(LOG_TAG, "Found incompatible accounts"); + Slog.e(LOG_TAG, "Found incompatible accounts"); } return !compatible; }); @@ -14462,7 +14441,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { return am.hasFeatures(account, features, null, null).getResult(); } catch (Exception e) { - Log.w(LOG_TAG, "Failed to get account feature", e); + Slog.w(LOG_TAG, "Failed to get account feature", e); return false; } } @@ -14696,24 +14675,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void sendNetworkLoggingNotificationLocked() { ensureLocked(); - final ActiveAdmin activeAdmin = getNetworkLoggingControllingAdminLocked(); - if (activeAdmin == null || !activeAdmin.isNetworkLoggingEnabled) { + // Send a network logging notification if the admin is a device owner, not profile owner. + final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); + if (deviceOwner == null || !deviceOwner.isNetworkLoggingEnabled) { return; } - if (activeAdmin.numNetworkLoggingNotifications + if (deviceOwner.numNetworkLoggingNotifications >= ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) { return; } final long now = System.currentTimeMillis(); - if (now - activeAdmin.lastNetworkLoggingNotificationTimeMs < MS_PER_DAY) { + if (now - deviceOwner.lastNetworkLoggingNotificationTimeMs < MS_PER_DAY) { return; } - activeAdmin.numNetworkLoggingNotifications++; - if (activeAdmin.numNetworkLoggingNotifications + deviceOwner.numNetworkLoggingNotifications++; + if (deviceOwner.numNetworkLoggingNotifications >= ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) { - activeAdmin.lastNetworkLoggingNotificationTimeMs = 0; + deviceOwner.lastNetworkLoggingNotificationTimeMs = 0; } else { - activeAdmin.lastNetworkLoggingNotificationTimeMs = now; + deviceOwner.lastNetworkLoggingNotificationTimeMs = now; } final PackageManagerInternal pm = mInjector.getPackageManagerInternal(); final Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG); @@ -14733,7 +14713,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .bigText(mContext.getString(R.string.network_logging_notification_text))) .build(); mInjector.getNotificationManager().notify(SystemMessage.NOTE_NETWORK_LOGGING, notification); - saveSettingsLocked(activeAdmin.getUserHandle().getIdentifier()); + saveSettingsLocked(deviceOwner.getUserHandle().getIdentifier()); } /** @@ -14761,8 +14741,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 0, // flags targetUserId); if (info == null || info.serviceInfo == null) { - Log.e(LOG_TAG, "Fail to look up the service: " + rawIntent - + " or user " + targetUserId + " is not running"); + Slog.e(LOG_TAG, "Fail to look up the service: %s or user %d is not running", rawIntent, + targetUserId); return null; } if (!expectedPackageName.equals(info.serviceInfo.packageName)) { @@ -15256,7 +15236,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mInjector.binderWithCleanCallingIdentity( () -> tm.addDevicePolicyOverrideApn(mContext, apnSetting)); } else { - Log.w(LOG_TAG, "TelephonyManager is null when trying to add override apn"); + Slog.w(LOG_TAG, "TelephonyManager is null when trying to add override apn"); return Telephony.Carriers.INVALID_APN_ID; } } @@ -15280,7 +15260,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mInjector.binderWithCleanCallingIdentity( () -> tm.modifyDevicePolicyOverrideApn(mContext, apnId, apnSetting)); } else { - Log.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn"); + Slog.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn"); return false; } } @@ -15323,7 +15303,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mInjector.binderWithCleanCallingIdentity( () -> tm.getDevicePolicyOverrideApns(mContext)); } - Log.w(LOG_TAG, "TelephonyManager is null when trying to get override apns"); + Slog.w(LOG_TAG, "TelephonyManager is null when trying to get override apns"); return Collections.emptyList(); } @@ -15476,8 +15456,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); - final String currentMode = - ConnectivityManager.getPrivateDnsMode(mContext.getContentResolver()); + final String currentMode = ConnectivityManager.getPrivateDnsMode(mContext); switch (currentMode) { case ConnectivityManager.PRIVATE_DNS_MODE_OFF: return PRIVATE_DNS_MODE_OFF; @@ -15824,8 +15803,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } if (!isPackageAllowedToAccessCalendarForUser(packageName, workProfileUserId)) { - Log.d(LOG_TAG, String.format("Package %s is not allowed to access cross-profile" - + "calendar APIs", packageName)); + Slog.d(LOG_TAG, "Package %s is not allowed to access cross-profile calendar APIs", + packageName); return false; } final Intent intent = new Intent( @@ -15839,7 +15818,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { mContext.startActivityAsUser(intent, UserHandle.of(workProfileUserId)); } catch (ActivityNotFoundException e) { - Log.e(LOG_TAG, "View event activity not found", e); + Slog.e(LOG_TAG, "View event activity not found", e); return false; } return true; @@ -15853,7 +15832,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { packageName, UserHandle.getUserId(callingUid)); return packageUid == callingUid; } catch (NameNotFoundException e) { - Log.d(LOG_TAG, "Calling package not found", e); + Slog.d(LOG_TAG, "Calling package not found", e); return false; } }); @@ -15963,8 +15942,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final long deadline = admin.mProfileOffDeadline; final int result = makeSuspensionReasons(admin.mSuspendPersonalApps, deadline != 0 && mInjector.systemCurrentTimeMillis() > deadline); - Slog.d(LOG_TAG, String.format("getPersonalAppsSuspendedReasons user: %d; result: %d", - mInjector.userHandleGetCallingUserId(), result)); + Slog.d(LOG_TAG, "getPersonalAppsSuspendedReasons user: %d; result: %d", + mInjector.userHandleGetCallingUserId(), result); return result; } } @@ -16048,9 +16027,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked); suspendedExplicitly = profileOwner.mSuspendPersonalApps; suspendedByTimeout = deadlineState == PROFILE_OFF_DEADLINE_REACHED; - Slog.d(LOG_TAG, String.format( - "Personal apps suspended explicitly: %b, deadline state: %d", - suspendedExplicitly, deadlineState)); + Slog.d(LOG_TAG, "Personal apps suspended explicitly: %b, deadline state: %d", + suspendedExplicitly, deadlineState); final int notificationState = unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState; updateProfileOffDeadlineNotificationLocked( @@ -16151,8 +16129,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (getUserData(userId).mAppsSuspended == suspended) { return; } - Slog.i(LOG_TAG, String.format("%s personal apps for user %d", - suspended ? "Suspending" : "Unsuspending", userId)); + Slog.i(LOG_TAG, "%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending", + userId); if (suspended) { suspendPersonalAppsInPackageManager(userId); @@ -16372,8 +16350,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkArgument(!TextUtils.isEmpty(organizationId), "Enterprise ID may not be empty."); - Log.i(LOG_TAG, - String.format("Setting Enterprise ID to %s for user %d", organizationId, userId)); + Slog.i(LOG_TAG, "Setting Enterprise ID to %s for user %d", organizationId, userId); final String ownerPackage; synchronized (getLockObject()) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java index 2959c10d5508..fa6ef006c61d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java @@ -207,7 +207,7 @@ public class RemoteBugreportManager { return true; } catch (RemoteException re) { // should never happen - Slog.e(LOG_TAG, re, "Failed to make remote calls to start bugreportremote service"); + Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re); return false; } finally { mInjector.binderRestoreCallingIdentity(callingIdentity); diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index c38d0b3cc7db..e3fbeddc3a5f 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -86,6 +86,9 @@ struct Constants { static constexpr auto maxBindDelay = 10000s; static constexpr auto bindDelayMultiplier = 10; static constexpr auto bindDelayJitterDivider = 10; + + // Max interval after system invoked the DL when readlog collection can be enabled. + static constexpr auto readLogsMaxInterval = 2h; }; static const Constants& constants() { @@ -290,6 +293,14 @@ void IncrementalService::IncFsMount::cleanupFilesystem(std::string_view root) { ::rmdir(path::c_str(root)); } +void IncrementalService::IncFsMount::setReadLogsEnabled(bool value) { + if (value) { + flags |= StorageFlags::ReadLogsEnabled; + } else { + flags &= ~StorageFlags::ReadLogsEnabled; + } +} + IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir) : mVold(sm.getVoldService()), mDataLoaderManager(sm.getDataLoaderManager()), @@ -406,7 +417,7 @@ void IncrementalService::onDump(int fd) { } bool IncrementalService::needStartDataLoaderLocked(IncFsMount& ifs) { - if (ifs.dataLoaderStub->params().packageName == Constants::systemPackage) { + if (ifs.dataLoaderStub->isSystemDataLoader()) { return true; } @@ -658,7 +669,7 @@ StorageId IncrementalService::createLinkedStorage(std::string_view mountPoint, return storageId; } -bool IncrementalService::startLoading(StorageId storage, +bool IncrementalService::startLoading(StorageId storageId, content::pm::DataLoaderParamsParcel&& dataLoaderParams, const DataLoaderStatusListener& statusListener, StorageHealthCheckParams&& healthCheckParams, @@ -666,12 +677,12 @@ bool IncrementalService::startLoading(StorageId storage, const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) { // Per Uid timeouts. if (!perUidReadTimeouts.empty()) { - setUidReadTimeouts(storage, perUidReadTimeouts); + setUidReadTimeouts(storageId, perUidReadTimeouts); } // Re-initialize DataLoader. std::unique_lock l(mLock); - const auto ifs = getIfsLocked(storage); + const auto ifs = getIfsLocked(storageId); if (!ifs) { return false; } @@ -686,6 +697,32 @@ bool IncrementalService::startLoading(StorageId storage, std::move(healthCheckParams), &healthListener); CHECK(dataLoaderStub); + if (dataLoaderStub->isSystemDataLoader()) { + // Readlogs from system dataloader (adb) can always be collected. + ifs->startLoadingTs = TimePoint::max(); + } else { + // Assign time when installation wants the DL to start streaming. + const auto startLoadingTs = mClock->now(); + ifs->startLoadingTs = startLoadingTs; + // Setup a callback to disable the readlogs after max interval. + addTimedJob(*mTimedQueue, storageId, Constants::readLogsMaxInterval, + [this, storageId, startLoadingTs]() { + const auto ifs = getIfs(storageId); + if (!ifs) { + LOG(WARNING) << "Can't disable the readlogs, invalid storageId: " + << storageId; + return; + } + if (ifs->startLoadingTs != startLoadingTs) { + LOG(INFO) << "Can't disable the readlogs, timestamp mismatch (new " + "installation?): " + << storageId; + return; + } + setStorageParams(*ifs, storageId, /*enableReadLogs=*/false); + }); + } + return dataLoaderStub->requestStart(); } @@ -735,11 +772,16 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog LOG(ERROR) << "setStorageParams failed, invalid storageId: " << storageId; return -EINVAL; } + return setStorageParams(*ifs, storageId, enableReadLogs); +} - const auto& params = ifs->dataLoaderStub->params(); +int IncrementalService::setStorageParams(IncFsMount& ifs, StorageId storageId, + bool enableReadLogs) { + const auto& params = ifs.dataLoaderStub->params(); if (enableReadLogs) { - if (!ifs->readLogsAllowed()) { - LOG(ERROR) << "setStorageParams failed, readlogs disabled for storageId: " << storageId; + if (!ifs.readLogsAllowed()) { + LOG(ERROR) << "setStorageParams failed, readlogs disallowed for storageId: " + << storageId; return -EPERM; } @@ -760,9 +802,19 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog << " check failed: " << status.toString8(); return fromBinderStatus(status); } + + // Check installation time. + const auto now = mClock->now(); + const auto startLoadingTs = ifs.startLoadingTs; + if (startLoadingTs <= now && now - startLoadingTs > Constants::readLogsMaxInterval) { + LOG(ERROR) << "setStorageParams failed, readlogs can't be enabled at this time, " + "storageId: " + << storageId; + return -EPERM; + } } - if (auto status = applyStorageParams(*ifs, enableReadLogs); !status.isOk()) { + if (auto status = applyStorageParams(ifs, enableReadLogs); !status.isOk()) { LOG(ERROR) << "applyStorageParams failed: " << status.toString8(); return fromBinderStatus(status); } @@ -2222,6 +2274,10 @@ sp<content::pm::IDataLoader> IncrementalService::DataLoaderStub::getDataLoader() return dataloader; } +bool IncrementalService::DataLoaderStub::isSystemDataLoader() const { + return (params().packageName == Constants::systemPackage); +} + bool IncrementalService::DataLoaderStub::requestCreate() { return setTargetStatus(IDataLoaderStatusListener::DATA_LOADER_CREATED); } diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 4eb513808342..bc441c792084 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -231,6 +231,7 @@ private: MountId id() const { return mId.load(std::memory_order_relaxed); } const content::pm::DataLoaderParamsParcel& params() const { return mParams; } + bool isSystemDataLoader() const; void setHealthListener(StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener* healthListener); long elapsedMsSinceOldestPendingRead(); @@ -330,6 +331,7 @@ private: StorageMap storages; BindMap bindPoints; DataLoaderStubPtr dataLoaderStub; + TimePoint startLoadingTs = {}; std::atomic<int> nextStorageDirNo{0}; const IncrementalService& incrementalService; @@ -348,12 +350,7 @@ private: void disallowReadLogs() { flags &= ~StorageFlags::ReadLogsAllowed; } int32_t readLogsAllowed() const { return (flags & StorageFlags::ReadLogsAllowed); } - void setReadLogsEnabled(bool value) { - if (value) - flags |= StorageFlags::ReadLogsEnabled; - else - flags &= ~StorageFlags::ReadLogsEnabled; - } + void setReadLogsEnabled(bool value); int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); } static void cleanupFilesystem(std::string_view root); @@ -411,6 +408,8 @@ private: IncFsMount::StorageMap::const_iterator storageIt, std::string_view path) const; int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode); + + int setStorageParams(IncFsMount& ifs, StorageId storageId, bool enableReadLogs); binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs); int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const; diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index 25b34b5669b8..bf798273a8a9 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -908,7 +908,7 @@ TEST_F(IncrementalServiceTest, testDataLoaderOnRestart) { EXPECT_CALL(*mDataLoader, start(_)).Times(6); EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); - EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2); + EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(3); TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, @@ -1119,7 +1119,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) { EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(2); EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(2); - EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(4); + EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(5); sp<NiceMock<MockStorageHealthListener>> listener{new NiceMock<MockStorageHealthListener>}; NiceMock<MockStorageHealthListener>* listenerMock = listener.get(); @@ -1292,6 +1292,147 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndDisabled) { ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM); } +TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndTimedOut) { + mVold->setIncFsMountOptionsSuccess(); + mAppOpsManager->checkPermissionSuccess(); + + const auto readLogsMaxInterval = 2h; + + EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + // Enabling and then disabling readlogs. + EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(2); + EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1); + // After setIncFsMountOptions succeeded expecting to start watching. + EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1); + // Not expecting callback removal. + EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); + EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(1); + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); + ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); + + // Disable readlogs callback present. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_EQ(mTimedQueue->mAfter, readLogsMaxInterval); + auto callback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + ASSERT_GE(mDataLoader->setStorageParams(true), 0); + // Now advance clock for 1hr. + mClock->advance(1h); + ASSERT_GE(mDataLoader->setStorageParams(true), 0); + // Now call the timed callback, it should turn off the readlogs. + callback(); + // Now advance clock for 2hrs. + mClock->advance(readLogsMaxInterval); + ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM); +} + +TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndNoTimedOutForSystem) { + mVold->setIncFsMountOptionsSuccess(); + mAppOpsManager->checkPermissionSuccess(); + + const auto readLogsMaxInterval = 2h; + + EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + // Enabling and then disabling readlogs. + EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3); + EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(0); + // After setIncFsMountOptions succeeded expecting to start watching. + EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1); + // Not expecting callback removal. + EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); + EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0); + // System data loader. + mDataLoaderParcel.packageName = "android"; + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); + ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); + + // No readlogs callback. + ASSERT_EQ(mTimedQueue->mAfter, 0ms); + ASSERT_EQ(mTimedQueue->mWhat, nullptr); + + ASSERT_GE(mDataLoader->setStorageParams(true), 0); + // Now advance clock for 1hr. + mClock->advance(1h); + ASSERT_GE(mDataLoader->setStorageParams(true), 0); + // Now advance clock for 2hrs. + mClock->advance(readLogsMaxInterval); + ASSERT_EQ(mDataLoader->setStorageParams(true), 0); +} + +TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndNewInstall) { + mVold->setIncFsMountOptionsSuccess(); + mAppOpsManager->checkPermissionSuccess(); + + const auto readLogsMaxInterval = 2h; + + EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + // Enabling and then disabling readlogs. + EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3); + EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1); + // After setIncFsMountOptions succeeded expecting to start watching. + EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1); + // Not expecting callback removal. + EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); + EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2); + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); + ASSERT_GE(storageId, 0); + + auto dataLoaderParcel = mDataLoaderParcel; + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(dataLoaderParcel), {}, {}, + {}, {})); + + // Disable readlogs callback present. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_EQ(mTimedQueue->mAfter, readLogsMaxInterval); + auto callback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + ASSERT_GE(mDataLoader->setStorageParams(true), 0); + // Now advance clock for 1.5hrs. + mClock->advance(90min); + ASSERT_GE(mDataLoader->setStorageParams(true), 0); + + // New installation. + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); + + // New callback present. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_EQ(mTimedQueue->mAfter, readLogsMaxInterval); + auto callback2 = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // Old callback should not disable readlogs (setIncFsMountOptions should be called only once). + callback(); + // Advance clock for another 1.5hrs. + mClock->advance(90min); + // Still success even it's 3hrs past first install. + ASSERT_GE(mDataLoader->setStorageParams(true), 0); + + // New one should disable. + callback2(); + // And timeout. + mClock->advance(90min); + ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM); +} + TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) { mVold->setIncFsMountOptionsSuccess(); mAppOpsManager->checkPermissionSuccess(); @@ -1675,7 +1816,7 @@ TEST_F(IncrementalServiceTest, testPerUidTimeoutsTooShort) { EXPECT_CALL(*mDataLoader, start(_)).Times(1); EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)).Times(0); - EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0); + EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = @@ -1702,6 +1843,9 @@ TEST_F(IncrementalServiceTest, testPerUidTimeoutsSuccess) { // Empty storage. mIncFs->countFilledBlocksEmpty(); + // Mark DataLoader as 'system' so that readlogs don't pollute the timed queue. + mDataLoaderParcel.packageName = "android"; + TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 4e23609bd774..592952354b8f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1327,7 +1327,7 @@ public final class SystemServer implements Dumpable { false); boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false); - boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1"); + boolean isEmulator = SystemProperties.get("ro.boot.qemu").equals("1"); boolean isWatch = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WATCH); diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java index 87b2c84a30f7..4c5bbebdfd45 100644 --- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java +++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java @@ -48,6 +48,8 @@ import com.android.server.infra.AbstractPerUserSystemService; import java.io.IOException; import java.io.OutputStream; import java.util.Objects; +import java.util.concurrent.CompletableFuture; + /** * Handles per-user requests received by @@ -60,6 +62,11 @@ public final class MusicRecognitionManagerPerUserService extends implements RemoteMusicRecognitionService.Callbacks { private static final String TAG = MusicRecognitionManagerPerUserService.class.getSimpleName(); + private static final String MUSIC_RECOGNITION_MANAGER_ATTRIBUTION_TAG = + "MusicRecognitionManagerService"; + private static final String KEY_MUSIC_RECOGNITION_SERVICE_ATTRIBUTION_TAG = + "android.media.musicrecognition.attributiontag"; + // Number of bytes per sample of audio (which is a short). private static final int BYTES_PER_SAMPLE = 2; private static final int MAX_STREAMING_SECONDS = 24; @@ -68,18 +75,24 @@ public final class MusicRecognitionManagerPerUserService extends @GuardedBy("mLock") private RemoteMusicRecognitionService mRemoteService; private final AppOpsManager mAppOpsManager; + private final String mAttributionMessage; - private String mAttributionTag; - private String mAttributionMessage; + // Service info of the remote MusicRecognitionService (which the audio gets forwarded to). private ServiceInfo mServiceInfo; + private CompletableFuture<String> mAttributionTagFuture; MusicRecognitionManagerPerUserService( @NonNull MusicRecognitionManagerService primary, @NonNull Object lock, int userId) { super(primary, lock, userId); - mAppOpsManager = getContext().getSystemService(AppOpsManager.class); + + // When attributing audio-access, this establishes that audio access is performed by + // MusicRecognitionManager (on behalf of the receiving service, whose attribution tag, + // provided by mAttributionTagFuture, is used for the actual calls to startProxyOp(...). + mAppOpsManager = getContext().createAttributionContext( + MUSIC_RECOGNITION_MANAGER_ATTRIBUTION_TAG).getSystemService(AppOpsManager.class); mAttributionMessage = String.format("MusicRecognitionManager.invokedByUid.%s", userId); - mAttributionTag = null; + mAttributionTagFuture = null; mServiceInfo = null; } @@ -126,10 +139,13 @@ public final class MusicRecognitionManagerPerUserService extends new MusicRecognitionServiceCallback(clientCallback), mMaster.isBindInstantServiceAllowed(), mMaster.verbose); + try { mServiceInfo = getContext().getPackageManager().getServiceInfo( - mRemoteService.getComponentName(), 0); + mRemoteService.getComponentName(), PackageManager.GET_META_DATA); + mAttributionTagFuture = mRemoteService.getAttributionTag(); + Slog.i(TAG, "Remote service bound: " + mRemoteService.getComponentName()); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "Service was not found.", e); } @@ -172,11 +188,13 @@ public final class MusicRecognitionManagerPerUserService extends ParcelFileDescriptor audioSink = clientPipe.second; ParcelFileDescriptor clientRead = clientPipe.first; - mMaster.mExecutorService.execute(() -> { - streamAudio(recognitionRequest, clientCallback, audioSink); - }); + mAttributionTagFuture.thenAcceptAsync( + tag -> { + streamAudio(tag, recognitionRequest, clientCallback, audioSink); + }, mMaster.mExecutorService); + // Send the pipe down to the lookup service while we write to it asynchronously. - mRemoteService.writeAudioToPipe(clientRead, recognitionRequest.getAudioFormat()); + mRemoteService.onAudioStreamStarted(clientRead, recognitionRequest.getAudioFormat()); } /** @@ -186,10 +204,12 @@ public final class MusicRecognitionManagerPerUserService extends * @param clientCallback the callback to notify on errors. * @param audioSink the sink to which to stream audio to. */ - private void streamAudio(@NonNull RecognitionRequest recognitionRequest, - IMusicRecognitionManagerCallback clientCallback, ParcelFileDescriptor audioSink) { + private void streamAudio(@Nullable String attributionTag, + @NonNull RecognitionRequest recognitionRequest, + IMusicRecognitionManagerCallback clientCallback, + ParcelFileDescriptor audioSink) { try { - startRecordAudioOp(); + startRecordAudioOp(attributionTag); } catch (SecurityException e) { // A security exception can occur if the MusicRecognitionService (receiving the audio) // does not (or does no longer) hold the necessary permissions to record audio. @@ -214,7 +234,7 @@ public final class MusicRecognitionManagerPerUserService extends Slog.e(TAG, "Audio streaming stopped.", e); } finally { audioRecord.release(); - finishRecordAudioOp(); + finishRecordAudioOp(attributionTag); try { clientCallback.onAudioStreamClosed(); } catch (RemoteException ignored) { @@ -323,23 +343,32 @@ public final class MusicRecognitionManagerPerUserService extends * Tracks that the RECORD_AUDIO operation started (attributes it to the service receiving the * audio). */ - private void startRecordAudioOp() { - mAppOpsManager.startProxyOp( + private void startRecordAudioOp(@Nullable String attributionTag) { + int status = mAppOpsManager.startProxyOp( Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)), mServiceInfo.applicationInfo.uid, mServiceInfo.packageName, - mAttributionTag, + attributionTag, mAttributionMessage); + // The above should already throw a SecurityException. This is just a fallback. + if (status != AppOpsManager.MODE_ALLOWED) { + throw new SecurityException(String.format( + "Failed to obtain RECORD_AUDIO permission (status: %d) for " + + "receiving service: %s", status, mServiceInfo.getComponentName())); + } + Slog.i(TAG, String.format( + "Starting audio streaming. Attributing to %s (%d) with tag '%s'", + mServiceInfo.packageName, mServiceInfo.applicationInfo.uid, attributionTag)); } /** Tracks that the RECORD_AUDIO operation finished. */ - private void finishRecordAudioOp() { + private void finishRecordAudioOp(@Nullable String attributionTag) { mAppOpsManager.finishProxyOp( Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)), mServiceInfo.applicationInfo.uid, mServiceInfo.packageName, - mAttributionTag); + attributionTag); } /** Establishes an audio stream from the DSP audio source. */ diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java index 6c7d673ffe11..99b448211492 100644 --- a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java +++ b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java @@ -20,15 +20,20 @@ import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.media.AudioFormat; +import android.media.musicrecognition.IMusicRecognitionAttributionTagCallback; import android.media.musicrecognition.IMusicRecognitionService; import android.media.musicrecognition.MusicRecognitionService; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.os.RemoteException; import android.text.format.DateUtils; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.server.musicrecognition.MusicRecognitionManagerPerUserService.MusicRecognitionServiceCallback; +import java.util.concurrent.CompletableFuture; + + /** Remote connection to an instance of {@link MusicRecognitionService}. */ public class RemoteMusicRecognitionService extends AbstractMultiplePendingRequestsRemoteService<RemoteMusicRecognitionService, @@ -81,9 +86,26 @@ public class RemoteMusicRecognitionService extends * Sends the given descriptor to the app's {@link MusicRecognitionService} to read the * audio. */ - public void writeAudioToPipe(@NonNull ParcelFileDescriptor fd, + public void onAudioStreamStarted(@NonNull ParcelFileDescriptor fd, @NonNull AudioFormat audioFormat) { scheduleAsyncRequest( binder -> binder.onAudioStreamStarted(fd, audioFormat, mServerCallback)); } + + + /** + * Returns the name of the <attribution> tag defined in the remote service's manifest. + */ + public CompletableFuture<String> getAttributionTag() { + CompletableFuture<String> attributionTagFuture = new CompletableFuture<String>(); + scheduleAsyncRequest( + binder -> binder.getAttributionTag( + new IMusicRecognitionAttributionTagCallback.Stub() { + @Override + public void onAttributionTag(String tag) throws RemoteException { + attributionTagFuture.complete(tag); + } + })); + return attributionTagFuture; + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index f2e85a700327..28940b34c82a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -27,6 +27,7 @@ import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC_WAKEUP; import static android.app.AlarmManager.WINDOW_EXACT; import static android.app.AlarmManager.WINDOW_HEURISTIC; +import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; @@ -47,17 +48,20 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.alarm.AlarmManagerService.ACTIVE_INDEX; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED; +import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_QUOTA; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WINDOW; +import static com.android.server.alarm.AlarmManagerService.Constants.KEY_CRASH_NON_CLOCK_APPS; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL; +import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_WINDOW; import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX; import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY; import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK; @@ -71,6 +75,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -81,6 +86,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import android.Manifest; import android.app.ActivityManager; @@ -408,7 +414,7 @@ public class AlarmManagerServiceTest { ArgumentCaptor<IAppOpsCallback> appOpsCallbackCaptor = ArgumentCaptor.forClass( IAppOpsCallback.class); try { - verify(mIAppOpsService).startWatchingMode(eq(AppOpsManager.OP_SCHEDULE_EXACT_ALARM), + verify(mIAppOpsService).startWatchingMode(eq(OP_SCHEDULE_EXACT_ALARM), isNull(), appOpsCallbackCaptor.capture()); } catch (RemoteException e) { // Not expected on a mock. @@ -445,12 +451,12 @@ public class AlarmManagerServiceTest { private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval, int flags, int callingUid) { - setTestAlarm(type, triggerTime, operation, interval, flags, callingUid, null); + setTestAlarm(type, triggerTime, 0, operation, interval, flags, callingUid, null); } - private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval, - int flags, int callingUid, Bundle idleOptions) { - mService.setImpl(type, triggerTime, WINDOW_EXACT, interval, operation, null, "test", flags, + private void setTestAlarm(int type, long triggerTime, long windowLength, + PendingIntent operation, long interval, int flags, int callingUid, Bundle idleOptions) { + mService.setImpl(type, triggerTime, windowLength, interval, operation, null, "test", flags, null, null, callingUid, TEST_CALLING_PACKAGE, idleOptions); } @@ -572,6 +578,7 @@ public class AlarmManagerServiceTest { setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW, 35); setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 40); setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 45); + setDeviceConfigLong(KEY_MIN_WINDOW, 50); assertEquals(5, mService.mConstants.MIN_FUTURITY); assertEquals(10, mService.mConstants.MIN_INTERVAL); assertEquals(15, mService.mConstants.MAX_INTERVAL); @@ -581,6 +588,7 @@ public class AlarmManagerServiceTest { assertEquals(35, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW); assertEquals(40, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION); assertEquals(45, mService.mConstants.LISTENER_TIMEOUT); + assertEquals(50, mService.mConstants.MIN_WINDOW); } @Test @@ -1644,6 +1652,10 @@ public class AlarmManagerServiceTest { getNewMockPendingIntent(), null, null, null, mock(AlarmManager.AlarmClockInfo.class)); + // exact + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, + 0, getNewMockPendingIntent(), null, null, null, null); + // exact, allow-while-idle mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null); @@ -1658,6 +1670,22 @@ public class AlarmManagerServiceTest { } @Test + public void exactBinderCallWhenChangeDisabled() throws Exception { + doReturn(false).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + final PendingIntent alarmPi = getNewMockPendingIntent(); + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, + 0, alarmPi, null, null, null, null); + + verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), + eq(alarmPi), isNull(), isNull(), + eq(FLAG_STANDALONE), isNull(), isNull(), + eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull()); + } + + @Test public void exactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception { doReturn(false).when( () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), @@ -1746,7 +1774,34 @@ public class AlarmManagerServiceTest { } @Test - public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException { + public void alarmClockBinderCallWithoutPermission() throws RemoteException { + setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true); + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); + + final PendingIntent alarmPi = getNewMockPendingIntent(); + final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); + try { + mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0, + alarmPi, null, null, null, alarmClock); + fail("alarm clock binder call succeeded without permission"); + } catch (SecurityException se) { + // Expected. + } + + verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); + } + + @Test + public void exactBinderCallWithPermission() throws RemoteException { doReturn(true).when( () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); @@ -1754,7 +1809,7 @@ public class AlarmManagerServiceTest { // Permission check is granted by default by the mock. final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, - FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + 0, alarmPi, null, null, null, null); verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, Manifest.permission.SCHEDULE_EXACT_ALARM)); @@ -1763,7 +1818,7 @@ public class AlarmManagerServiceTest { final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), - eq(FLAG_ALLOW_WHILE_IDLE | FLAG_STANDALONE), isNull(), isNull(), + eq(FLAG_STANDALONE), isNull(), isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue()); @@ -1772,7 +1827,7 @@ public class AlarmManagerServiceTest { } @Test - public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException { + public void exactBinderCallWithAllowlist() throws RemoteException { doReturn(true).when( () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); @@ -1784,7 +1839,7 @@ public class AlarmManagerServiceTest { final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, - FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + 0, alarmPi, null, null, null, null); verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, Manifest.permission.SCHEDULE_EXACT_ALARM)); @@ -1793,7 +1848,30 @@ public class AlarmManagerServiceTest { final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), - eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(), + eq(FLAG_STANDALONE), isNull(), isNull(), + eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + System.out.println("what got captured: " + bundleCaptor.getValue()); + } + + @Test + public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + // Permission check is granted by default by the mock. + final PendingIntent alarmPi = getNewMockPendingIntent(); + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, + FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + + verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); + + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), + eq(alarmPi), isNull(), isNull(), + eq(FLAG_ALLOW_WHILE_IDLE | FLAG_STANDALONE), isNull(), isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue()); @@ -1802,44 +1880,83 @@ public class AlarmManagerServiceTest { } @Test - public void inexactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException { + public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException { doReturn(true).when( () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); - + // If permission is denied, only then allowlist will be checked. + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); + final PendingIntent alarmPi = getNewMockPendingIntent(); - mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0, + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, - Manifest.permission.SCHEDULE_EXACT_ALARM), never()); + Manifest.permission.SCHEDULE_EXACT_ALARM)); verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid())); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); - verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L), - eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(), - isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), + eq(alarmPi), isNull(), isNull(), + eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(), + eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue()); final int type = idleOptions.getTemporaryAppAllowlistType(); - assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type); + // App is on power allowlist, doesn't need explicit FGS grant in broadcast options. + assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED, type); } @Test - public void inexactAllowWhileIdleBinderCallWithoutAllowlist() throws RemoteException { + public void exactBinderCallsWithoutPermissionWithoutAllowlist() throws RemoteException { + setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true); doReturn(true).when( () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false); + + final PendingIntent alarmPi = getNewMockPendingIntent(); + try { + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, + 0, alarmPi, null, null, null, null); + fail("exact binder call succeeded without permission"); + } catch (SecurityException se) { + // Expected. + } + try { + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, + FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + fail("exact, allow-while-idle binder call succeeded without permission"); + } catch (SecurityException se) { + // Expected. + } + verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM), times(2)); + verify(mDeviceIdleInternal, times(2)).isAppOnWhitelist(anyInt()); + } + + @Test + public void inexactAllowWhileIdleBinderCall() throws RemoteException { + // Both permission and power exemption status don't matter for these alarms. + // We only want to test that the flags and idleOptions are correct. + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, Manifest.permission.SCHEDULE_EXACT_ALARM), never()); - verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid())); + verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L), @@ -1852,13 +1969,104 @@ public class AlarmManagerServiceTest { } @Test + public void binderCallWithUserAllowlist() throws RemoteException { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); + when(mAppStateTracker.isUidPowerSaveUserExempt(Process.myUid())).thenReturn(true); + + final PendingIntent alarmPi = getNewMockPendingIntent(); + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, + FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), + eq(alarmPi), isNull(), isNull(), + eq(FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_STANDALONE), isNull(), isNull(), + eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull()); + } + + @Test + public void minWindow() { + final long minWindow = 73; + setDeviceConfigLong(KEY_MIN_WINDOW, minWindow); + + // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC. + for (int window = 1; window <= minWindow; window++) { + final PendingIntent pi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null); + + assertEquals(1, mService.mAlarmStore.size()); + final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0); + assertEquals(minWindow, a.windowLength); + } + } + + @Test + public void opScheduleExactAlarmRevoked() throws Exception { + when(mIAppOpsService.checkOperation(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(AppOpsManager.MODE_ERRORED); + mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); + assertAndHandleMessageSync(REMOVE_EXACT_ALARMS); + verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, + TEST_CALLING_PACKAGE); + } + + @Test + public void removeExactAlarmsOnPermissionRevoked() { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + // basic exact alarm + setTestAlarm(ELAPSED_REALTIME, 0, 0, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, + null); + // exact and allow-while-idle alarm + setTestAlarm(ELAPSED_REALTIME, 0, 0, getNewMockPendingIntent(), 0, FLAG_ALLOW_WHILE_IDLE, + TEST_CALLING_UID, null); + // alarm clock + setWakeFromIdle(RTC_WAKEUP, 0, getNewMockPendingIntent()); + + final PendingIntent inexact = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 0, 10, inexact, 0, 0, TEST_CALLING_UID, null); + + final PendingIntent inexactAwi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 0, 10, inexactAwi, 0, FLAG_ALLOW_WHILE_IDLE, + TEST_CALLING_UID, null); + + final PendingIntent exactButDifferentUid = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 0, 0, exactButDifferentUid, 0, 0, TEST_CALLING_UID + 5, + null); + assertEquals(6, mService.mAlarmStore.size()); + + mService.removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE); + + final ArrayList<Alarm> remaining = mService.mAlarmStore.asList(); + assertEquals(3, remaining.size()); + assertTrue("Basic inexact alarm removed", + remaining.removeIf(a -> a.matches(inexact, null))); + assertTrue("Inexact allow-while-idle alarm removed", + remaining.removeIf(a -> a.matches(inexactAwi, null))); + assertTrue("Alarm from different uid removed", + remaining.removeIf(a -> a.matches(exactButDifferentUid, null))); + + // Mock should return false by default. + verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID)); + } + + @Test public void idleOptionsSentOnExpiration() throws Exception { final long triggerTime = mNowElapsedTest + 5000; final PendingIntent alarmPi = getNewMockPendingIntent(); final Bundle idleOptions = new Bundle(); idleOptions.putChar("TEST_CHAR_KEY", 'x'); idleOptions.putInt("TEST_INT_KEY", 53); - setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi, 0, 0, TEST_CALLING_UID, + setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, 0, alarmPi, 0, 0, TEST_CALLING_UID, idleOptions); mNowElapsedTest = mTestTimer.getElapsed(); @@ -1885,6 +2093,7 @@ public class AlarmManagerServiceTest { assertTrue(i + "th PendingIntent missing: ", alarmsBefore.removeIf(a -> a.matches(pi, null))); } + assertEquals(BatchingAlarmStore.TAG, mService.mAlarmStore.getName()); setDeviceConfigBoolean(KEY_LAZY_BATCHING, true); @@ -1895,6 +2104,7 @@ public class AlarmManagerServiceTest { assertTrue(i + "th PendingIntent missing: ", alarmsAfter.removeIf(a -> a.matches(pi, null))); } + assertEquals(LazyAlarmStore.TAG, mService.mAlarmStore.getName()); } @After 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 29db7409131e..7e4bc1e371b2 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 @@ -1880,6 +1880,144 @@ public class QuotaControllerTest { } @Test + public void testIsWithinEJQuotaLocked_NeverApp() { + JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1); + setStandbyBucket(NEVER_INDEX, js); + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); + } + } + + @Test + public void testIsWithinEJQuotaLocked_Charging() { + setCharging(); + JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_Charging", 1); + synchronized (mQuotaController.mLock) { + assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); + } + } + + @Test + public void testIsWithinEJQuotaLocked_UnderDuration() { + setDischarging(); + JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_UnderDuration", 1); + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); + synchronized (mQuotaController.mLock) { + assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); + } + } + + @Test + public void testIsWithinEJQuotaLocked_OverDuration() { + setDischarging(); + JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_OverDuration", 1); + setStandbyBucket(FREQUENT_INDEX, js); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS); + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true); + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); + } + } + + @Test + public void testIsWithinEJQuotaLocked_TimingSession() { + setDischarging(); + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 8 * MINUTE_IN_MILLIS); + + JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TimingSession", 1); + for (int i = 0; i < 25; ++i) { + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, + 2), true); + + synchronized (mQuotaController.mLock) { + setStandbyBucket(ACTIVE_INDEX, js); + assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions", + i < 19, mQuotaController.isWithinEJQuotaLocked(js)); + + setStandbyBucket(WORKING_INDEX, js); + assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions", + i < 14, mQuotaController.isWithinEJQuotaLocked(js)); + + setStandbyBucket(FREQUENT_INDEX, js); + assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions", + i < 12, mQuotaController.isWithinEJQuotaLocked(js)); + + setStandbyBucket(RARE_INDEX, js); + assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions", + i < 9, mQuotaController.isWithinEJQuotaLocked(js)); + + setStandbyBucket(RESTRICTED_INDEX, js); + assertEquals("Restricted has incorrect quota status with " + (i + 1) + " sessions", + i < 7, mQuotaController.isWithinEJQuotaLocked(js)); + } + } + } + + /** + * Tests that Timers properly track sessions when an app is added and removed from the temp + * allowlist. + */ + @Test + public void testIsWithinEJQuotaLocked_TempAllowlisting() { + setDischarging(); + JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TempAllowlisting", 1); + setStandbyBucket(FREQUENT_INDEX, js); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS); + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true); + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + final long gracePeriodMs = 15 * SECOND_IN_MILLIS; + setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs); + Handler handler = mQuotaController.getHandler(); + spyOn(handler); + + // Apps on the temp allowlist should be able to schedule & start EJs, even if they're out + // of quota (as long as they are in the temp allowlist grace period). + mTempAllowlistListener.onAppAdded(mSourceUid); + synchronized (mQuotaController.mLock) { + assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + mTempAllowlistListener.onAppRemoved(mSourceUid); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + // Still in grace period + synchronized (mQuotaController.mLock) { + assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); + } + advanceElapsedClock(6 * SECOND_IN_MILLIS); + // Out of grace period. + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); + } + } + + @Test public void testMaybeScheduleCleanupAlarmLocked() { // No sessions saved yet. synchronized (mQuotaController.mLock) { @@ -3881,6 +4019,55 @@ public class QuotaControllerTest { } @Test + public void testGetRemainingEJExecutionTimeLocked_IncrementalTimingSessions() { + setDischarging(); + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS); + + for (int i = 1; i <= 25; ++i) { + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, + 2), true); + + synchronized (mQuotaController.mLock) { + setStandbyBucket(ACTIVE_INDEX); + assertEquals("Active has incorrect remaining EJ time with " + i + " sessions", + (20 - i) * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(WORKING_INDEX); + assertEquals("Working has incorrect remaining EJ time with " + i + " sessions", + (15 - i) * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(FREQUENT_INDEX); + assertEquals("Frequent has incorrect remaining EJ time with " + i + " sessions", + (13 - i) * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(RARE_INDEX); + assertEquals("Rare has incorrect remaining EJ time with " + i + " sessions", + (10 - i) * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + + setStandbyBucket(RESTRICTED_INDEX); + assertEquals("Restricted has incorrect remaining EJ time with " + i + " sessions", + (5 - i) * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + } + + @Test public void testGetTimeUntilEJQuotaConsumedLocked_NoHistory() { final long[] limits = mQuotaController.getEJLimitsMs(); for (int i = 0; i < limits.length; ++i) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 44b9f44fc843..872b95548655 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -1114,6 +1114,30 @@ public class FullScreenMagnificationControllerTest { argThat(closeTo(newEndSpec))); } + @Test + public void testSetScale_toMagnifying_shouldNotifyActivatedState() { + setScaleToMagnifying(); + + verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(true)); + } + + @Test + public void testReset_afterMagnifying_shouldNotifyDeactivatedState() { + setScaleToMagnifying(); + + mFullScreenMagnificationController.reset(DISPLAY_0, mAnimationCallback); + verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(false)); + } + + private void setScaleToMagnifying() { + register(DISPLAY_0); + float scale = 2.0f; + PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + + mFullScreenMagnificationController.setScale(DISPLAY_0, scale, pivotPoint.x, pivotPoint.y, + false, SERVICE_ID_1); + } + private void initMockWindowManager() { for (int i = 0; i < DISPLAY_COUNT; i++) { when(mMockWindowManager.setMagnificationCallbacks(eq(i), any())).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index 0c3640c4eeb0..84c76b77018d 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -16,6 +16,9 @@ package com.android.server.accessibility.magnification; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; + import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID; import static org.junit.Assert.assertEquals; @@ -24,6 +27,7 @@ import static org.junit.Assert.assertTrue; 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.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; @@ -283,6 +287,27 @@ public class MagnificationControllerTest { } @Test + public void onWindowMagnificationActivationState_windowActivated_logWindowDuration() { + mMagnificationController.onWindowMagnificationActivationState(true); + + mMagnificationController.onWindowMagnificationActivationState(false); + + verify(mMagnificationController).logMagnificationUsageState( + eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), anyLong()); + } + + @Test + public void + onFullScreenMagnificationActivationState_fullScreenActivated_logFullScreenDuration() { + mMagnificationController.onFullScreenMagnificationActivationState(true); + + mMagnificationController.onFullScreenMagnificationActivationState(false); + + verify(mMagnificationController).logMagnificationUsageState( + eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN), anyLong()); + } + + @Test public void onTouchInteractionStart_fullScreenAndCapabilitiesAll_showMagnificationButton() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java index f26c86c69587..955217c7d93f 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java @@ -392,6 +392,23 @@ public class WindowMagnificationManagerTest { assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } + @Test + public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); + + verify(mMockCallback).onWindowMagnificationActivationState(eq(true)); + } + + @Test + public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); + mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, true); + + verify(mMockCallback).onWindowMagnificationActivationState(eq(false)); + } + private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) { final int len = pointersLocation.length; diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java index 8d54ead75761..73a2febf72aa 100644 --- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java @@ -17,8 +17,10 @@ package com.android.server.am; import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL; +import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_BT; import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU; import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY; +import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -39,6 +41,7 @@ import android.util.SparseArray; import androidx.test.InstrumentationRegistry; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.os.PowerProfile; import org.junit.Before; import org.junit.Test; @@ -61,7 +64,7 @@ public class BatteryExternalStatsWorkerTest { public void setUp() { final Context context = InstrumentationRegistry.getContext(); - mBatteryStatsImpl = new TestBatteryStatsImpl(); + mBatteryStatsImpl = new TestBatteryStatsImpl(context); mPowerStatsInternal = new TestPowerStatsInternal(); mBatteryExternalStatsWorker = new BatteryExternalStatsWorker(new TestInjector(context), mBatteryStatsImpl); @@ -72,13 +75,24 @@ public class BatteryExternalStatsWorkerTest { final int numCpuClusters = 4; final int numOther = 3; - final IntArray tempAllIds = new IntArray(); // Add some energy consumers used by BatteryExternalStatsWorker. + final IntArray tempAllIds = new IntArray(); + final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0, "display"); tempAllIds.add(displayId); mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345); + final int wifiId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.WIFI, 0, + "wifi"); + tempAllIds.add(wifiId); + mPowerStatsInternal.incrementEnergyConsumption(wifiId, 23456); + + final int btId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.BLUETOOTH, 0, + "bt"); + tempAllIds.add(btId); + mPowerStatsInternal.incrementEnergyConsumption(btId, 34567); + final int[] cpuClusterIds = new int[numCpuClusters]; for (int i = 0; i < numCpuClusters; i++) { cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer( @@ -109,6 +123,18 @@ public class BatteryExternalStatsWorkerTest { assertEquals(1, displayResults.length); assertEquals(displayId, displayResults[0].id); + final EnergyConsumerResult[] wifiResults = + mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_WIFI).getNow(null); + // Results should only have the wifi energy consumer + assertEquals(1, wifiResults.length); + assertEquals(wifiId, wifiResults[0].id); + + final EnergyConsumerResult[] bluetoothResults = + mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_BT).getNow(null); + // Results should only have the bluetooth energy consumer + assertEquals(1, bluetoothResults.length); + assertEquals(btId, bluetoothResults[0].id); + final EnergyConsumerResult[] cpuResults = mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_CPU).getNow(null); // Results should only have the cpu cluster energy consumers @@ -148,6 +174,9 @@ public class BatteryExternalStatsWorkerTest { } public class TestBatteryStatsImpl extends BatteryStatsImpl { + public TestBatteryStatsImpl(Context context) { + mPowerProfile = new PowerProfile(context, true /* forTest */); + } } public class TestPowerStatsInternal extends PowerStatsInternal { 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 6890ed1688bb..c84c1cf8c8f1 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 @@ -415,9 +415,7 @@ public class AppSearchImplTest { + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) { GenericDocument document = - new GenericDocument.Builder<>("uri" + i, "type") - .setNamespace("namespace") - .build(); + new GenericDocument.Builder<>("namespace", "uri" + i, "type").build(); mAppSearchImpl.putDocument("package", "database", document); } @@ -477,7 +475,7 @@ public class AppSearchImplTest { // Insert document GenericDocument document = - new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build(); + new GenericDocument.Builder<>("namespace", "uri", "type").build(); mAppSearchImpl.putDocument("package", "database", document); // Rewrite SearchSpec @@ -517,11 +515,11 @@ public class AppSearchImplTest { // Insert documents GenericDocument document1 = - new GenericDocument.Builder<>("uri", "typeA").setNamespace("namespace").build(); + new GenericDocument.Builder<>("namespace", "uri", "typeA").build(); mAppSearchImpl.putDocument("package", "database1", document1); GenericDocument document2 = - new GenericDocument.Builder<>("uri", "typeB").setNamespace("namespace").build(); + new GenericDocument.Builder<>("namespace", "uri", "typeB").build(); mAppSearchImpl.putDocument("package", "database2", document2); // Rewrite SearchSpec @@ -561,7 +559,7 @@ public class AppSearchImplTest { // Insert document GenericDocument document = - new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build(); + new GenericDocument.Builder<>("namespace", "uri", "type").build(); mAppSearchImpl.putDocument("package", "database", document); // If 'allowedPrefixedSchemas' is empty, this returns false since there's nothing to @@ -614,7 +612,7 @@ public class AppSearchImplTest { // Insert package1 document GenericDocument document = - new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build(); + new GenericDocument.Builder<>("namespace", "uri", "schema1").build(); mAppSearchImpl.putDocument("package1", "database1", document); // No query filters specified, package2 shouldn't be able to query for package1's documents. @@ -625,14 +623,13 @@ public class AppSearchImplTest { assertThat(searchResultPage.getResults()).isEmpty(); // Insert package2 document - document = - new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build(); + document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build(); mAppSearchImpl.putDocument("package2", "database2", document); // No query filters specified. package2 should only get its own documents back. searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec); assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document); + assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document); } /** @@ -665,7 +662,7 @@ public class AppSearchImplTest { // Insert package1 document GenericDocument document = - new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build(); + new GenericDocument.Builder<>("namespace", "uri", "schema1").build(); mAppSearchImpl.putDocument("package1", "database1", document); // "package1" filter specified, but package2 shouldn't be able to query for package1's @@ -680,8 +677,7 @@ public class AppSearchImplTest { assertThat(searchResultPage.getResults()).isEmpty(); // Insert package2 document - document = - new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build(); + document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build(); mAppSearchImpl.putDocument("package2", "database2", document); // "package2" filter specified, package2 should only get its own documents back. @@ -692,7 +688,7 @@ public class AppSearchImplTest { .build(); searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec); assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document); + assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document); } @Test @@ -1073,7 +1069,7 @@ public class AppSearchImplTest { for (SearchResult result : searchResultPage.getResults()) { assertThat(result.getPackageName()).isEqualTo("com.package.foo"); assertThat(result.getDatabaseName()).isEqualTo("databaseName"); - assertThat(result.getDocument()) + assertThat(result.getGenericDocument()) .isEqualTo( GenericDocumentToProtoConverter.toGenericDocument( strippedDocumentProto.build())); @@ -1128,9 +1124,7 @@ public class AppSearchImplTest { appSearchImpl.putDocument( "package", "database", - new GenericDocument.Builder<>("uri", "type") - .setNamespace("namespace") - .build()); + new GenericDocument.Builder<>("namespace", "uri", "type").build()); }); expectThrows( 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 194be3761903..70e1e05174ef 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 @@ -36,23 +36,23 @@ public class GenericDocumentToProtoConverterTest { private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7}; private static final GenericDocument DOCUMENT_PROPERTIES_1 = new GenericDocument.Builder<GenericDocument.Builder<?>>( - "sDocumentProperties1", "sDocumentPropertiesSchemaType1") + "namespace", "sDocumentProperties1", "sDocumentPropertiesSchemaType1") .setCreationTimestampMillis(12345L) .build(); private static final GenericDocument DOCUMENT_PROPERTIES_2 = new GenericDocument.Builder<GenericDocument.Builder<?>>( - "sDocumentProperties2", "sDocumentPropertiesSchemaType2") + "namespace", "sDocumentProperties2", "sDocumentPropertiesSchemaType2") .setCreationTimestampMillis(6789L) .build(); @Test public void testDocumentProtoConvert() { GenericDocument document = - new GenericDocument.Builder<GenericDocument.Builder<?>>("uri1", "schemaType1") + new GenericDocument.Builder<GenericDocument.Builder<?>>( + "namespace", "uri1", "schemaType1") .setCreationTimestampMillis(5L) .setScore(1) .setTtlMillis(1L) - .setNamespace("namespace") .setPropertyLong("longKey1", 1L) .setPropertyDouble("doubleKey1", 1.0) .setPropertyBoolean("booleanKey1", true) 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 0b1c120d8a1e..d07211fe2028 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 @@ -93,10 +93,10 @@ public class SnippetTest { assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); assertThat(match.getFullText()).isEqualTo(propertyValueString); assertThat(match.getExactMatch()).isEqualTo(exactMatch); - assertThat(match.getExactMatchPosition()) + assertThat(match.getExactMatchRange()) .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 29, /*upper=*/ 32)); assertThat(match.getFullText()).isEqualTo(propertyValueString); - assertThat(match.getSnippetPosition()) + assertThat(match.getSnippetRange()) .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32)); assertThat(match.getSnippet()).isEqualTo(window); } @@ -210,20 +210,20 @@ public class SnippetTest { SearchResult.MatchInfo match1 = result.getMatches().get(0); assertThat(match1.getPropertyPath()).isEqualTo("sender.name"); assertThat(match1.getFullText()).isEqualTo("Test Name Jr."); - assertThat(match1.getExactMatchPosition()) + assertThat(match1.getExactMatchRange()) .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4)); assertThat(match1.getExactMatch()).isEqualTo("Test"); - assertThat(match1.getSnippetPosition()) + assertThat(match1.getSnippetRange()) .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9)); assertThat(match1.getSnippet()).isEqualTo("Test Name"); SearchResult.MatchInfo match2 = result.getMatches().get(1); assertThat(match2.getPropertyPath()).isEqualTo("sender.email"); assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com"); - assertThat(match2.getExactMatchPosition()) + assertThat(match2.getExactMatchRange()) .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20)); assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com"); - assertThat(match2.getSnippetPosition()) + assertThat(match2.getSnippetRange()) .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20)); assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com"); } diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index c1b6101b5e58..bf621b1f56cb 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -30,11 +30,13 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.Handler; +import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,6 +45,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest +@Presubmit @RunWith(AndroidJUnit4.class) public class AutomaticBrightnessControllerTest { private static final float BRIGHTNESS_MIN_FLOAT = 0.0f; @@ -55,9 +58,11 @@ public class AutomaticBrightnessControllerTest { private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false; private static final int DISPLAY_ID = 0; private static final int LAYER_STACK = 0; + private static final int LIGHT_SENSOR_WARMUP_TIME = 0; private Context mContext; private LogicalDisplay mLogicalDisplay; + private AutomaticBrightnessController mController; @Mock SensorManager mSensorManager; @Mock BrightnessMappingStrategy mBrightnessMappingStrategy; @@ -67,7 +72,6 @@ public class AutomaticBrightnessControllerTest { @Mock DisplayDevice mDisplayDevice; @Mock HighBrightnessModeController mHbmController; - private static final int LIGHT_SENSOR_WARMUP_TIME = 0; @Before public void setUp() { // Share classloader to allow package private access. @@ -78,6 +82,15 @@ public class AutomaticBrightnessControllerTest { mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice); } + @After + public void tearDown() { + if (mController != null) { + // Stop the update Brightness loop. + mController.stop(); + mController = null; + } + } + private AutomaticBrightnessController setupController(Sensor lightSensor) { AutomaticBrightnessController controller = new AutomaticBrightnessController( new AutomaticBrightnessController.Injector() { @@ -94,7 +107,9 @@ public class AutomaticBrightnessControllerTest { mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mLogicalDisplay, mContext, mHbmController ); - controller.setLoggingEnabled(true); + + when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT); + when(mHbmController.getCurrentBrightnessMin()).thenReturn(BRIGHTNESS_MIN_FLOAT); // Configure the brightness controller and grab an instance of the sensor listener, // through which we can deliver fake (for test) sensor values. @@ -108,7 +123,7 @@ public class AutomaticBrightnessControllerTest { @Test public void testNoHysteresisAtMinBrightness() throws Exception { Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); - AutomaticBrightnessController controller = setupController(lightSensor); + mController = setupController(lightSensor); ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); @@ -135,7 +150,7 @@ public class AutomaticBrightnessControllerTest { // Send new sensor value and verify listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1)); - assertEquals(normalizedBrightness1, controller.getAutomaticScreenBrightness(), 0.001f); + assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), 0.001f); // Set up system to return 0.0f (minimum possible brightness) as a brightness value float lux2 = 10.0f; @@ -149,13 +164,13 @@ public class AutomaticBrightnessControllerTest { // Send new sensor value and verify listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2)); - assertEquals(normalizedBrightness2, controller.getAutomaticScreenBrightness(), 0.001f); + assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), 0.001f); } @Test public void testNoHysteresisAtMaxBrightness() throws Exception { Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); - AutomaticBrightnessController controller = setupController(lightSensor); + mController = setupController(lightSensor); ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); @@ -181,7 +196,7 @@ public class AutomaticBrightnessControllerTest { // Send new sensor value and verify listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1)); - assertEquals(normalizedBrightness1, controller.getAutomaticScreenBrightness(), 0.001f); + assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), 0.001f); // Set up system to return 1.0f as a brightness value (brightness_max) @@ -196,13 +211,13 @@ public class AutomaticBrightnessControllerTest { // Send new sensor value and verify listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2)); - assertEquals(normalizedBrightness2, controller.getAutomaticScreenBrightness(), 0.001f); + assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), 0.001f); } @Test public void testUserAddUserDataPoint() throws Exception { Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); - AutomaticBrightnessController controller = setupController(lightSensor); + mController = setupController(lightSensor); ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); @@ -214,7 +229,7 @@ public class AutomaticBrightnessControllerTest { listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000)); // User sets brightness to 100 - controller.configure(true /* enable */, null /* configuration */, + mController.configure(true /* enable */, null /* configuration */, 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT); diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java index 893ce9e6c70c..bdf94f3a2882 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java @@ -81,6 +81,7 @@ import java.util.concurrent.TimeUnit; public class BrightnessTrackerTest { private static final float DEFAULT_INITIAL_BRIGHTNESS = 2.5f; private static final boolean DEFAULT_COLOR_SAMPLING_ENABLED = true; + private static final String DEFAULT_DISPLAY_ID = "123"; private static final float FLOAT_DELTA = 0.01f; private BrightnessTracker mTracker; @@ -285,18 +286,20 @@ public class BrightnessTrackerTest { @Test public void testBrightnessEvent() { - final int brightness = 20; + final float brightness = 0.5f; + final String displayId = "1234"; startTracker(mTracker); mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f)); mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2)); - notifyBrightnessChanged(mTracker, brightness); + notifyBrightnessChanged(mTracker, brightness, displayId); List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); mTracker.stop(); assertEquals(1, events.size()); BrightnessChangeEvent event = events.get(0); assertEquals(mInjector.currentTimeMillis(), event.timeStamp); + assertEquals(displayId, event.uniqueDisplayId); assertEquals(1, event.luxValues.length); assertEquals(1.0f, event.luxValues[0], FLOAT_DELTA); assertEquals(mInjector.currentTimeMillis() - TimeUnit.SECONDS.toMillis(2), @@ -314,6 +317,7 @@ public class BrightnessTrackerTest { public void testBrightnessFullPopulatedEvent() { final int initialBrightness = 230; final int brightness = 130; + final String displayId = "1234"; mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1); mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3333); @@ -326,7 +330,7 @@ public class BrightnessTrackerTest { batteryChangeEvent(30, 60)); mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f)); final long sensorTime = mInjector.currentTimeMillis(); - notifyBrightnessChanged(mTracker, brightness); + notifyBrightnessChanged(mTracker, brightness, displayId); List<BrightnessChangeEvent> eventsNoPackage = mTracker.getEvents(0, false).getList(); List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); @@ -335,6 +339,7 @@ public class BrightnessTrackerTest { assertEquals(1, events.size()); BrightnessChangeEvent event = events.get(0); assertEquals(event.timeStamp, mInjector.currentTimeMillis()); + assertEquals(displayId, event.uniqueDisplayId); assertArrayEquals(new float[] {1000.0f}, event.luxValues, 0.01f); assertArrayEquals(new long[] {sensorTime}, event.luxTimestamps); assertEquals(brightness, event.brightness, FLOAT_DELTA); @@ -364,7 +369,7 @@ public class BrightnessTrackerTest { final int systemUpdatedBrightness = 20; notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/, 0.5f /*powerBrightnessFactor(*/, false /*isUserSetBrightness*/, - false /*isDefaultBrightnessConfig*/); + false /*isDefaultBrightnessConfig*/, DEFAULT_DISPLAY_ID); List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); // No events because we filtered out our change. assertEquals(0, events.size()); @@ -455,6 +460,7 @@ public class BrightnessTrackerTest { + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\" " + "reduceBrightColors=\"false\" reduceBrightColorsStrength=\"40\" " + "reduceBrightColorsOffset=\"0\"\n" + + "uniqueDisplayId=\"123\"" + "lux=\"32.2,31.1\" luxTimestamps=\"" + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"" + "defaultConfig=\"true\" powerSaveFactor=\"0.5\" userPoint=\"true\" />" @@ -465,6 +471,7 @@ public class BrightnessTrackerTest { + "batteryLevel=\"0.5\" nightMode=\"true\" colorTemperature=\"3235\" " + "reduceBrightColors=\"true\" reduceBrightColorsStrength=\"40\" " + "reduceBrightColorsOffset=\"0\"\n" + + "uniqueDisplayId=\"456\"" + "lux=\"132.2,131.1\" luxTimestamps=\"" + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"" + "colorSampleDuration=\"3456\" colorValueBuckets=\"123,598,23,19\"/>" @@ -476,6 +483,7 @@ public class BrightnessTrackerTest { + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\" " + "reduceBrightColors=\"false\" reduceBrightColorsStrength=\"40\" " + "reduceBrightColorsOffset=\"0\"\n" + + "uniqueDisplayId=\"789\"" + "lux=\"32.2,31.1\" luxTimestamps=\"" + Long.toString(twoMonthsAgo) + "," + Long.toString(twoMonthsAgo) + "\"/>" + "</events>"; @@ -485,6 +493,7 @@ public class BrightnessTrackerTest { BrightnessChangeEvent event = events.get(0); assertEquals(someTimeAgo, event.timeStamp); assertEquals(194.2, event.brightness, FLOAT_DELTA); + assertEquals("123", event.uniqueDisplayId); assertArrayEquals(new float[] {32.2f, 31.1f}, event.luxValues, FLOAT_DELTA); assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps); assertEquals(32.333, event.lastBrightness, FLOAT_DELTA); @@ -503,6 +512,7 @@ public class BrightnessTrackerTest { event = events.get(0); assertEquals(someTimeAgo, event.timeStamp); assertEquals(71, event.brightness, FLOAT_DELTA); + assertEquals("456", event.uniqueDisplayId); assertArrayEquals(new float[] {132.2f, 131.1f}, event.luxValues, FLOAT_DELTA); assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps); assertEquals(32, event.lastBrightness, FLOAT_DELTA); @@ -575,6 +585,7 @@ public class BrightnessTrackerTest { @Test public void testWriteThenRead() throws Exception { final int brightness = 20; + final String displayId = "1234"; mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1); mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339); @@ -593,7 +604,7 @@ public class BrightnessTrackerTest { mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3)); notifyBrightnessChanged(mTracker, brightness, true /*userInitiated*/, 0.5f /*powerBrightnessFactor*/, true /*hasUserBrightnessPoints*/, - false /*isDefaultBrightnessConfig*/); + false /*isDefaultBrightnessConfig*/, displayId); ByteArrayOutputStream baos = new ByteArrayOutputStream(); mTracker.writeEventsLocked(baos); mTracker.stop(); @@ -607,6 +618,7 @@ public class BrightnessTrackerTest { assertEquals(1, events.size()); BrightnessChangeEvent event = events.get(0); + assertEquals(displayId, event.uniqueDisplayId); assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA); assertArrayEquals(new long[] {firstSensorTime, secondSensorTime}, event.luxTimestamps); assertEquals(brightness, event.brightness, FLOAT_DELTA); @@ -678,6 +690,7 @@ public class BrightnessTrackerTest { builder.setTimeStamp(345L); builder.setPackageName("com.example"); builder.setUserId(12); + builder.setUniqueDisplayId("9876"); float[] luxValues = new float[2]; luxValues[0] = 3000.0f; luxValues[1] = 4000.0f; @@ -710,6 +723,7 @@ public class BrightnessTrackerTest { assertEquals(event.timeStamp, event2.timeStamp); assertEquals(event.packageName, event2.packageName); assertEquals(event.userId, event2.userId); + assertEquals(event.uniqueDisplayId, event2.uniqueDisplayId); assertArrayEquals(event.luxValues, event2.luxValues, FLOAT_DELTA); assertArrayEquals(event.luxTimestamps, event2.luxTimestamps); assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA); @@ -773,7 +787,7 @@ public class BrightnessTrackerTest { long eventTime = mInjector.currentTimeMillis(); mTracker.notifyBrightnessChanged(brightness, true /*userInitiated*/, 1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/, - false /*isDefaultBrightnessConfig*/); + false /*isDefaultBrightnessConfig*/, DEFAULT_DISPLAY_ID); // Time passes before handler can run. mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2)); @@ -791,6 +805,35 @@ public class BrightnessTrackerTest { assertEquals(eventTime, event.timeStamp); } + @Test + public void testDisplayIdChange() { + float firstBrightness = 0.5f; + float secondBrightness = 0.75f; + String firstDisplayId = "123"; + String secondDisplayId = "456"; + + startTracker(mTracker); + mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f)); + + notifyBrightnessChanged(mTracker, firstBrightness, firstDisplayId); + mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2)); + List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); + assertEquals(1, events.size()); + BrightnessChangeEvent firstEvent = events.get(0); + assertEquals(firstDisplayId, firstEvent.uniqueDisplayId); + assertEquals(firstBrightness, firstEvent.brightness, 0.001f); + + notifyBrightnessChanged(mTracker, secondBrightness, secondDisplayId); + mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2)); + events = mTracker.getEvents(0, true).getList(); + assertEquals(2, events.size()); + BrightnessChangeEvent secondEvent = events.get(1); + assertEquals(secondDisplayId, secondEvent.uniqueDisplayId); + assertEquals(secondBrightness, secondEvent.brightness, 0.001f); + + mTracker.stop(); + } + private InputStream getInputStream(String data) { return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)); } @@ -831,16 +874,21 @@ public class BrightnessTrackerTest { } private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness) { + notifyBrightnessChanged(tracker, brightness, DEFAULT_DISPLAY_ID); + } + + private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness, + String displayId) { notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/, 1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/, - false /*isDefaultBrightnessConfig*/); + false /*isDefaultBrightnessConfig*/, displayId); } private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness, boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness, - boolean isDefaultBrightnessConfig) { + boolean isDefaultBrightnessConfig, String displayId) { tracker.notifyBrightnessChanged(brightness, userInitiated, powerBrightnessFactor, - isUserSetBrightness, isDefaultBrightnessConfig); + isUserSetBrightness, isDefaultBrightnessConfig, displayId); mInjector.waitForHandler(); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java index 605f781b23df..53b4b491410d 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java @@ -19,7 +19,6 @@ package com.android.server.hdmi; import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; -import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; import static com.google.common.truth.Truth.assertThat; @@ -51,6 +50,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.Collections; import java.util.concurrent.TimeUnit; /** Tests for {@link ActiveSourceAction} */ @@ -84,7 +84,8 @@ public class PowerStatusMonitorActionTest { when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager); when(mIPowerManagerMock.isInteractive()).thenReturn(true); - mHdmiControlService = new HdmiControlService(mContextSpy) { + mHdmiControlService = new HdmiControlService(mContextSpy, + Collections.singletonList(HdmiDeviceInfo.DEVICE_TV)) { @Override AudioManager getAudioManager() { return new AudioManager() { @@ -140,6 +141,7 @@ public class PowerStatusMonitorActionTest { mNativeWrapper.setPhysicalAddress(mPhysicalAddress); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); } @Test @@ -152,7 +154,7 @@ public class PowerStatusMonitorActionTest { mTestLooper.dispatchAll(); HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( - ADDR_TV, + mTvDevice.mAddress, ADDR_PLAYBACK_1); assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); @@ -191,7 +193,7 @@ public class PowerStatusMonitorActionTest { mTestLooper.dispatchAll(); HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( - ADDR_TV, + mTvDevice.mAddress, ADDR_PLAYBACK_1); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus); @@ -220,12 +222,12 @@ public class PowerStatusMonitorActionTest { mTestLooper.dispatchAll(); HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( - ADDR_TV, + mTvDevice.mAddress, ADDR_PLAYBACK_1); assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( - ADDR_TV, + mTvDevice.mAddress, ADDR_PLAYBACK_2); assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus2); } @@ -245,13 +247,13 @@ public class PowerStatusMonitorActionTest { mTestLooper.dispatchAll(); HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( - ADDR_TV, + mTvDevice.mAddress, ADDR_PLAYBACK_1); assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( - ADDR_TV, + mTvDevice.mAddress, ADDR_PLAYBACK_2); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus2); @@ -265,7 +267,7 @@ public class PowerStatusMonitorActionTest { } private void reportPowerStatus(int logicalAddress, boolean broadcast, int powerStatus) { - int destination = broadcast ? ADDR_BROADCAST : ADDR_TV; + int destination = broadcast ? ADDR_BROADCAST : mTvDevice.mAddress; HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( logicalAddress, destination, powerStatus); diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java index 60390dc3995e..b2dacab26365 100644 --- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java +++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java @@ -261,6 +261,7 @@ public class UsageStatsDatabaseTest { // mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed); assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible); + assertEquals(us1.mLastTimeComponentUsed, us2.mLastTimeComponentUsed); assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground); assertEquals(us1.mTotalTimeVisible, us2.mTotalTimeVisible); assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index f5d831bbe73e..67fe7bf436b7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -117,6 +117,7 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; +import android.view.View; import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -1699,6 +1700,20 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testFindScrollCaptureTargetWindow_cantReceiveKeys() { + DisplayContent display = createNewDisplay(); + Task stack = createTaskStackOnDisplay(display); + Task task = createTaskInStack(stack, 0 /* userId */); + WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window"); + WindowState invisible = createWindow(null, TYPE_APPLICATION, "invisible"); + invisible.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false + + WindowState result = display.findScrollCaptureTargetWindow(null, + ActivityTaskManager.INVALID_TASK_ID); + assertEquals(activityWindow, result); + } + + @Test public void testFindScrollCaptureTargetWindow_taskId() { DisplayContent display = createNewDisplay(); Task stack = createTaskStackOnDisplay(display); @@ -1711,6 +1726,19 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys() { + DisplayContent display = createNewDisplay(); + Task stack = createTaskStackOnDisplay(display); + Task task = createTaskInStack(stack, 0 /* userId */); + WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window"); + window.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false + WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot"); + + WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId); + assertEquals(window, result); + } + + @Test public void testEnsureActivitiesVisibleNotRecursive() { final TaskDisplayArea mockTda = mock(TaskDisplayArea.class); final boolean[] called = { false }; 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 36cf9c9fb0cf..5c7e58036aba 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -161,7 +161,8 @@ public class SizeCompatTests extends WindowTestsBase { final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds(); final float aspectRatio = 1.2f; - mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = aspectRatio; + mActivity.info.setMaxAspectRatio(aspectRatio); + mActivity.info.setMinAspectRatio(aspectRatio); prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED); final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds(); @@ -780,6 +781,139 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) + public void testOverrideMinAspectRatioMedium() { + setUpDisplaySizeWithApp(1000, 1200); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override forces the activity into a 3:2 aspect ratio + assertEquals(1200, activity.getBounds().height()); + assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, + activity.getBounds().width(), 0.5); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) + public void testOverrideMinAspectRatioLowerThanManifest() { + setUpDisplaySizeWithApp(1400, 1600); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(2f) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override should have no effect, because the manifest aspect ratio is + // larger (2:1) + assertEquals(1600, activity.getBounds().height()); + assertEquals(800, activity.getBounds().width()); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) + public void testOverrideMinAspectRatioLargerThanManifest() { + setUpDisplaySizeWithApp(1400, 1600); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(1.1f) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override should have no effect, because the manifest aspect ratio is + // larger (2:1) + assertEquals(1600, activity.getBounds().height()); + assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, + activity.getBounds().width(), 0.5); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) + public void testOverrideMinAspectRatioLarge() { + setUpDisplaySizeWithApp(1500, 1600); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override forces the activity into a 16:9 aspect ratio + assertEquals(1600, activity.getBounds().height()); + assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, + activity.getBounds().width(), 0.5); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) + public void testOverrideMinAspectRatio_Both() { + // If multiple override aspect ratios are set, we should use the largest one + + setUpDisplaySizeWithApp(1400, 1600); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override forces the activity into a 16:9 aspect ratio + assertEquals(1600, activity.getBounds().height()); + assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, + activity.getBounds().width(), 0.5); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) + public void testOverrideMinAspectRatioWithoutGlobalOverride() { + // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without + // OVERRIDE_MIN_ASPECT_RATIO being also set. + + setUpDisplaySizeWithApp(1000, 1200); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override should have no effect + assertEquals(1200, activity.getBounds().height()); + assertEquals(1000, activity.getBounds().width()); + } + + @Test public void testLaunchWithFixedRotationTransform() { final int dw = 1000; final int dh = 2500; @@ -872,7 +1006,7 @@ public class SizeCompatTests extends WindowTestsBase { // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed // orientation letterbox. mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(1.1f); - mActivity.info.minAspectRatio = 3; + mActivity.info.setMinAspectRatio(3); prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT); final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); @@ -889,7 +1023,8 @@ public class SizeCompatTests extends WindowTestsBase { // Activity bounds should respect minimum aspect ratio for activity. assertEquals(displayBounds.height(), activityBounds.height()); - assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.minAspectRatio), + assertEquals((int) Math.rint(displayBounds.height() + / mActivity.info.getManifestMinAspectRatio()), activityBounds.width()); } @@ -918,7 +1053,8 @@ public class SizeCompatTests extends WindowTestsBase { // Activity bounds should respect maximum aspect ratio for activity. assertEquals(displayBounds.height(), activityBounds.height()); - assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.maxAspectRatio), + assertEquals((int) Math.rint(displayBounds.height() + / mActivity.info.getMaxAspectRatio()), activityBounds.width()); } @@ -1098,7 +1234,8 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(newActivity.inSizeCompatMode()); assertEquals(displayBounds.height(), newActivityBounds.height()); - assertEquals((long) Math.rint(newActivityBounds.height() / newActivity.info.maxAspectRatio), + assertEquals((long) Math.rint(newActivityBounds.height() + / newActivity.info.getMaxAspectRatio()), newActivityBounds.width()); } @@ -1347,7 +1484,7 @@ public class SizeCompatTests extends WindowTestsBase { : RESIZE_MODE_RESIZEABLE; activity.mVisibleRequested = true; if (maxAspect >= 0) { - activity.info.maxAspectRatio = maxAspect; + activity.info.setMaxAspectRatio(maxAspect); } if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { activity.info.screenOrientation = screenOrientation; 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 cac69657f5cc..21536a6e1cfb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -136,7 +136,10 @@ class TestDisplayContent extends DisplayContent { final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, mInfo, DEFAULT_DISPLAY_ADJUSTMENTS); final TestDisplayContent newDisplay = createInternal(display); - + // Ensure letterbox aspect ratio is not overridden on any device target. + // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by + // the below method, is set on some device form factors. + mService.mWindowManager.setFixedOrientationLetterboxAspectRatio(0); // disable the normal system decorations final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy(); spyOn(displayPolicy); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java index 001afe3e2637..b3a07454c1a0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java @@ -22,10 +22,11 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.MergedConfiguration; import android.view.DragEvent; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindow; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.ScrollCaptureResponse; import android.window.ClientWindowFrames; import com.android.internal.os.IResultReceiver; @@ -116,7 +117,15 @@ public class TestIWindow extends IWindow.Stub { } @Override - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) throws RemoteException { + public void requestScrollCapture(IScrollCaptureResponseListener listener) + throws RemoteException { + try { + listener.onScrollCaptureResponse( + new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build()); + + } catch (RemoteException ex) { + // ignore + } } @Override 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 4a7784cf1b36..779457bb3e2f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -714,6 +714,7 @@ class WindowTestsBase extends SystemServiceTestsBase { private int mLaunchMode; private int mResizeMode = RESIZE_MODE_RESIZEABLE; private float mMaxAspectRatio; + private float mMinAspectRatio; private boolean mSupportsSizeChanges; private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED; private boolean mLaunchTaskBehind = false; @@ -793,6 +794,11 @@ class WindowTestsBase extends SystemServiceTestsBase { return this; } + ActivityBuilder setMinAspectRatio(float minAspectRatio) { + mMinAspectRatio = minAspectRatio; + return this; + } + ActivityBuilder setSupportsSizeChanges(boolean supportsSizeChanges) { mSupportsSizeChanges = supportsSizeChanges; return this; @@ -884,7 +890,8 @@ class WindowTestsBase extends SystemServiceTestsBase { aInfo.flags |= mActivityFlags; aInfo.launchMode = mLaunchMode; aInfo.resizeMode = mResizeMode; - aInfo.maxAspectRatio = mMaxAspectRatio; + aInfo.setMaxAspectRatio(mMaxAspectRatio); + aInfo.setMinAspectRatio(mMinAspectRatio); aInfo.supportsSizeChanges = mSupportsSizeChanges; aInfo.screenOrientation = mScreenOrientation; aInfo.configChanges |= mConfigChanges; diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java index 78b14779d6b3..ec4c5fc4a846 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsProto.java +++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java @@ -147,6 +147,10 @@ final class UsageStatsProto { stats.mTotalTimeVisible = proto.readLong( IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS); break; + case (int) IntervalStatsProto.UsageStats.LAST_TIME_COMPONENT_USED_MS: + stats.mLastTimeComponentUsed = statsOut.beginTime + proto.readLong( + IntervalStatsProto.UsageStats.LAST_TIME_COMPONENT_USED_MS); + break; } } proto.end(token); @@ -345,6 +349,9 @@ final class UsageStatsProto { usageStats.mLastTimeVisible, stats.beginTime); proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS, usageStats.mTotalTimeVisible); + UsageStatsProtoV2.writeOffsetTimestamp(proto, + IntervalStatsProto.UsageStats.LAST_TIME_COMPONENT_USED_MS, + usageStats.mLastTimeComponentUsed, stats.beginTime); proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount); try { writeChooserCounts(proto, usageStats); diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java index e6d28417d3ca..5c5667cf0389 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java +++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java @@ -90,6 +90,10 @@ final class UsageStatsProtoV2 { stats.mTotalTimeVisible = proto.readLong( UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS); break; + case (int) UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS: + stats.mLastTimeComponentUsed = beginTime + proto.readLong( + UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS); + break; case ProtoInputStream.NO_MORE_FIELDS: return stats; } @@ -312,6 +316,8 @@ final class UsageStatsProtoV2 { writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS, stats.mLastTimeVisible, beginTime); proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS, stats.mTotalTimeVisible); + writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS, + stats.mLastTimeComponentUsed, beginTime); proto.write(UsageStatsObfuscatedProto.APP_LAUNCH_COUNT, stats.mAppLaunchCount); try { writeChooserCounts(proto, stats); diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index f35b9e2ce0ed..22b4f4ef57e5 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -1003,6 +1003,8 @@ class UserUsageStatsService { formatElapsedTime(usageStats.mTotalTimeVisible, prettyDates)); pw.printPair("lastTimeVisible", formatDateTime(usageStats.mLastTimeVisible, prettyDates)); + pw.printPair("lastTimeComponentUsed", + formatDateTime(usageStats.mLastTimeComponentUsed, prettyDates)); pw.printPair("totalTimeFS", formatElapsedTime(usageStats.mTotalTimeForegroundServiceUsed, prettyDates)); pw.printPair("lastTimeFS", diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java index 5fb6b3381360..f5357b19c4de 100644 --- a/telecomm/java/android/telecom/CallDiagnosticService.java +++ b/telecomm/java/android/telecom/CallDiagnosticService.java @@ -27,8 +27,6 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; import android.os.RemoteException; -import android.telephony.Annotation; -import android.telephony.ims.ImsReasonInfo; import android.util.ArrayMap; import com.android.internal.telecom.ICallDiagnosticService; @@ -59,7 +57,7 @@ import java.util.concurrent.Executor; * </pre> * <p> * <h2>Threading Model</h2> - * By default, all incoming IPC from Telecom in this service and in the {@link DiagnosticCall} + * By default, all incoming IPC from Telecom in this service and in the {@link CallDiagnostics} * instances will take place on the main thread. You can override {@link #getExecutor()} in your * implementation to provide your own {@link Executor}. * @hide @@ -116,26 +114,28 @@ public abstract class CallDiagnosticService extends Service { } /** - * Listens to events raised by a {@link DiagnosticCall}. + * Listens to events raised by a {@link CallDiagnostics}. */ - private android.telecom.DiagnosticCall.Listener mDiagnosticCallListener = - new android.telecom.DiagnosticCall.Listener() { + private CallDiagnostics.Listener mDiagnosticCallListener = + new CallDiagnostics.Listener() { @Override - public void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall, - @DiagnosticCall.MessageType int message, int value) { - handleSendDeviceToDeviceMessage(diagnosticCall, message, value); + public void onSendDeviceToDeviceMessage(CallDiagnostics callDiagnostics, + @CallDiagnostics.MessageType int message, int value) { + handleSendDeviceToDeviceMessage(callDiagnostics, message, value); } @Override - public void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId, + public void onDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, + int messageId, CharSequence message) { - handleDisplayDiagnosticMessage(diagnosticCall, messageId, message); + handleDisplayDiagnosticMessage(callDiagnostics, messageId, message); } @Override - public void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) { - handleClearDiagnosticMessage(diagnosticCall, messageId); + public void onClearDiagnosticMessage(CallDiagnostics callDiagnostics, + int messageId) { + handleClearDiagnosticMessage(callDiagnostics, messageId); } }; @@ -149,7 +149,7 @@ public abstract class CallDiagnosticService extends Service { * Map which tracks the Telecom calls received from the Telecom stack. */ private final Map<String, Call.Details> mCallByTelecomCallId = new ArrayMap<>(); - private final Map<String, DiagnosticCall> mDiagnosticCallByTelecomCallId = new ArrayMap<>(); + private final Map<String, CallDiagnostics> mDiagnosticCallByTelecomCallId = new ArrayMap<>(); private final Object mLock = new Object(); private ICallDiagnosticServiceAdapter mAdapter; @@ -177,7 +177,7 @@ public abstract class CallDiagnosticService extends Service { * executor you want to use for incoming IPC. * * @return the {@link Executor} to use for incoming IPC from Telecom to - * {@link CallDiagnosticService} and {@link DiagnosticCall}. + * {@link CallDiagnosticService} and {@link CallDiagnostics}. */ @SuppressLint("OnNameExpected") @NonNull public Executor getExecutor() { @@ -188,30 +188,30 @@ public abstract class CallDiagnosticService extends Service { * Telecom calls this method on the {@link CallDiagnosticService} with details about a new call * which was added to Telecom. * <p> - * The {@link CallDiagnosticService} returns an implementation of {@link DiagnosticCall} to be + * The {@link CallDiagnosticService} returns an implementation of {@link CallDiagnostics} to be * used for the lifespan of this call. * <p> * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see * {@link CallDiagnosticService#getExecutor()} for more information. * * @param call The details of the new call. - * @return An instance of {@link DiagnosticCall} which the {@link CallDiagnosticService} + * @return An instance of {@link CallDiagnostics} which the {@link CallDiagnosticService} * provides to be used for the lifespan of the call. - * @throws IllegalArgumentException if a {@code null} {@link DiagnosticCall} is returned. + * @throws IllegalArgumentException if a {@code null} {@link CallDiagnostics} is returned. */ - public abstract @NonNull DiagnosticCall onInitializeDiagnosticCall(@NonNull + public abstract @NonNull CallDiagnostics onInitializeCallDiagnostics(@NonNull android.telecom.Call.Details call); /** - * Telecom calls this method when a previous created {@link DiagnosticCall} is no longer needed. - * This happens when Telecom is no longer tracking the call in question. + * Telecom calls this method when a previous created {@link CallDiagnostics} is no longer + * needed. This happens when Telecom is no longer tracking the call in question. * <p> * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see * {@link CallDiagnosticService#getExecutor()} for more information. * * @param call The diagnostic call which is no longer tracked by Telecom. */ - public abstract void onRemoveDiagnosticCall(@NonNull DiagnosticCall call); + public abstract void onRemoveCallDiagnostics(@NonNull CallDiagnostics call); /** * Telecom calls this method when the audio routing or available audio route information @@ -260,35 +260,35 @@ public abstract class CallDiagnosticService extends Service { } getExecutor().execute(() -> { - DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails); - if (diagnosticCall == null) { + CallDiagnostics callDiagnostics = onInitializeCallDiagnostics(newCallDetails); + if (callDiagnostics == null) { throw new IllegalArgumentException( "A valid DiagnosticCall instance was not provided."); } synchronized (mLock) { - diagnosticCall.setListener(mDiagnosticCallListener); - diagnosticCall.setCallId(telecomCallId); - mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall); + callDiagnostics.setListener(mDiagnosticCallListener); + callDiagnostics.setCallId(telecomCallId); + mDiagnosticCallByTelecomCallId.put(telecomCallId, callDiagnostics); } }); } /** * Handles an update to {@link Call.Details} notified by Telecom. - * Caches the call details and notifies the {@link DiagnosticCall} of the change via - * {@link DiagnosticCall#onCallDetailsChanged(Call.Details)}. + * Caches the call details and notifies the {@link CallDiagnostics} of the change via + * {@link CallDiagnostics#onCallDetailsChanged(Call.Details)}. * @param parcelableCall the new parceled call details from Telecom. */ private void handleCallUpdated(@NonNull ParcelableCall parcelableCall) { String telecomCallId = parcelableCall.getId(); Log.i(this, "handleCallUpdated: callId=%s - updated", telecomCallId); Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall); - DiagnosticCall diagnosticCall; + CallDiagnostics callDiagnostics; synchronized (mLock) { - diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId); + callDiagnostics = mDiagnosticCallByTelecomCallId.get(telecomCallId); mCallByTelecomCallId.put(telecomCallId, newCallDetails); } - getExecutor().execute(() -> diagnosticCall.handleCallUpdated(newCallDetails)); + getExecutor().execute(() -> callDiagnostics.handleCallUpdated(newCallDetails)); } /** @@ -302,37 +302,37 @@ public abstract class CallDiagnosticService extends Service { mCallByTelecomCallId.remove(telecomCallId); } - DiagnosticCall diagnosticCall; + CallDiagnostics callDiagnostics; synchronized (mLock) { if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) { - diagnosticCall = mDiagnosticCallByTelecomCallId.remove(telecomCallId); + callDiagnostics = mDiagnosticCallByTelecomCallId.remove(telecomCallId); } else { - diagnosticCall = null; + callDiagnostics = null; } } // Inform the service of the removed call. - if (diagnosticCall != null) { - getExecutor().execute(() -> onRemoveDiagnosticCall(diagnosticCall)); + if (callDiagnostics != null) { + getExecutor().execute(() -> onRemoveCallDiagnostics(callDiagnostics)); } } /** * Handles an incoming device to device message received from Telecom. Notifies the - * {@link DiagnosticCall} via {@link DiagnosticCall#onReceiveDeviceToDeviceMessage(int, int)}. + * {@link CallDiagnostics} via {@link CallDiagnostics#onReceiveDeviceToDeviceMessage(int, int)}. * @param callId * @param message * @param value */ private void handleReceivedD2DMessage(@NonNull String callId, int message, int value) { Log.i(this, "handleReceivedD2DMessage: callId=%s, msg=%d/%d", callId, message, value); - DiagnosticCall diagnosticCall; + CallDiagnostics callDiagnostics; synchronized (mLock) { - diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId); + callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId); } - if (diagnosticCall != null) { + if (callDiagnostics != null) { getExecutor().execute( - () -> diagnosticCall.onReceiveDeviceToDeviceMessage(message, value)); + () -> callDiagnostics.onReceiveDeviceToDeviceMessage(message, value)); } } @@ -345,12 +345,12 @@ public abstract class CallDiagnosticService extends Service { private void handleCallDisconnected(@NonNull String callId, @NonNull DisconnectCause disconnectCause) { Log.i(this, "handleCallDisconnected: call=%s; cause=%s", callId, disconnectCause); - DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId); + CallDiagnostics callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId); CharSequence message; if (disconnectCause.getImsReasonInfo() != null) { - message = diagnosticCall.onCallDisconnected(disconnectCause.getImsReasonInfo()); + message = callDiagnostics.onCallDisconnected(disconnectCause.getImsReasonInfo()); } else { - message = diagnosticCall.onCallDisconnected( + message = callDiagnostics.onCallDisconnected( disconnectCause.getTelephonyDisconnectCause(), disconnectCause.getTelephonyPreciseDisconnectCause()); } @@ -375,15 +375,15 @@ public abstract class CallDiagnosticService extends Service { } /** - * Handles a request from a {@link DiagnosticCall} to send a device to device message (received - * via {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}. - * @param diagnosticCall + * Handles a request from a {@link CallDiagnostics} to send a device to device message (received + * via {@link CallDiagnostics#sendDeviceToDeviceMessage(int, int)}. + * @param callDiagnostics * @param message * @param value */ - private void handleSendDeviceToDeviceMessage(@NonNull DiagnosticCall diagnosticCall, + private void handleSendDeviceToDeviceMessage(@NonNull CallDiagnostics callDiagnostics, int message, int value) { - String callId = diagnosticCall.getCallId(); + String callId = callDiagnostics.getCallId(); try { mAdapter.sendDeviceToDeviceMessage(callId, message, value); Log.i(this, "handleSendDeviceToDeviceMessage: call=%s; msg=%d/%d", callId, message, @@ -395,15 +395,15 @@ public abstract class CallDiagnosticService extends Service { } /** - * Handles a request from a {@link DiagnosticCall} to display an in-call diagnostic message. - * Originates from {@link DiagnosticCall#displayDiagnosticMessage(int, CharSequence)}. - * @param diagnosticCall + * Handles a request from a {@link CallDiagnostics} to display an in-call diagnostic message. + * Originates from {@link CallDiagnostics#displayDiagnosticMessage(int, CharSequence)}. + * @param callDiagnostics * @param messageId * @param message */ - private void handleDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId, + private void handleDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId, CharSequence message) { - String callId = diagnosticCall.getCallId(); + String callId = callDiagnostics.getCallId(); try { mAdapter.displayDiagnosticMessage(callId, messageId, message); Log.i(this, "handleDisplayDiagnosticMessage: call=%s; msg=%d/%s", callId, messageId, @@ -415,14 +415,14 @@ public abstract class CallDiagnosticService extends Service { } /** - * Handles a request from a {@link DiagnosticCall} to clear a previously shown diagnostic + * Handles a request from a {@link CallDiagnostics} to clear a previously shown diagnostic * message. - * Originates from {@link DiagnosticCall#clearDiagnosticMessage(int)}. - * @param diagnosticCall + * Originates from {@link CallDiagnostics#clearDiagnosticMessage(int)}. + * @param callDiagnostics * @param messageId */ - private void handleClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) { - String callId = diagnosticCall.getCallId(); + private void handleClearDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId) { + String callId = callDiagnostics.getCallId(); try { mAdapter.clearDiagnosticMessage(callId, messageId); Log.i(this, "handleClearDiagnosticMessage: call=%s; msg=%d", callId, messageId); diff --git a/telecomm/java/android/telecom/CallDiagnostics.java b/telecomm/java/android/telecom/CallDiagnostics.java new file mode 100644 index 000000000000..3356431f17b1 --- /dev/null +++ b/telecomm/java/android/telecom/CallDiagnostics.java @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.telephony.Annotation; +import android.telephony.CallQuality; +import android.telephony.ims.ImsReasonInfo; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + +/** + * {@link CallDiagnostics} provides a way for a {@link CallDiagnosticService} to receive diagnostic + * information about a mobile call on the device. A {@link CallDiagnostics} instance is similar to + * a {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic + * and messaging capabilities not present on a {@link Call}. The {@link CallDiagnosticService} + * creates a {@link CallDiagnostics} for each {@link Call} on the device. This means that for each + * in progress call on the device, the {@link CallDiagnosticService} will create an instance of + * {@link CallDiagnostics}. + * <p> + * The {@link CallDiagnosticService} can generate mid-call diagnostic messages using the + * {@link #displayDiagnosticMessage(int, CharSequence)} API which provides the user with valuable + * information about conditions impacting their call and corrective actions. For example, if the + * {@link CallDiagnosticService} determines that conditions on the call are degrading, it can inform + * the user that the call may soon drop and that they can try using a different calling method + * (e.g. VOIP or WIFI). + * <h2>Threading Model</h2> + * All incoming IPC from Telecom in this class will use the same {@link Executor} as the + * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more + * information. + * @hide + */ +@SystemApi +public abstract class CallDiagnostics { + + /** + * @hide + */ + public interface Listener { + /** + * Used to inform the {@link CallDiagnosticService} of a request to send a D2d message + * @param callDiagnostics the call the message is from. + * @param message the message type + * @param value the message value + */ + void onSendDeviceToDeviceMessage(CallDiagnostics callDiagnostics, int message, int value); + + /** + * Used to inform the {@link CallDiagnosticService} of a request to display a diagnostic + * message. + * @param callDiagnostics the call the message pertains to. + * @param messageId an identifier for the message. + * @param message the message to display. + */ + void onDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId, + CharSequence message); + + /** + * Used to inform the {@link CallDiagnosticService} that a previously shown message is no + * longer pertinent. + * @param callDiagnostics the call the message pertains to. + * @param messageId the ID of the previously posted message. + */ + void onClearDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId); + } + + /** + * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via + * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type + * used for the current call. The call network type communicated here is an intentional + * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which + * removes some of the resolution inherent in those values; the + * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is + * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for + * efficiency of transport. For a discussion on the necessity of this simplification, see + * {@link #sendDeviceToDeviceMessage(int, int)}. + * <p> + * Valid values are below: + * <UL> + * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI> + * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI> + * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI> + * </UL> + */ + public static final int MESSAGE_CALL_NETWORK_TYPE = 1; + + /** + * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via + * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec + * used for the current call. + * <p> + * The audio codec communicated here is an intentional simplification of the + * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common + * variants of these audio codecs. Other variants of these codecs are reported as the next + * closest variant. For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec + * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}. + * For a discussion on the necessity of this simplification, see + * {@link #sendDeviceToDeviceMessage(int, int)}. + * <p> + * Valid values: + * <UL> + * <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI> + * <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI> + * <LI>{@link Connection#AUDIO_CODEC_AMR}</LI> + * </UL> + */ + public static final int MESSAGE_CALL_AUDIO_CODEC = 2; + + /** + * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via + * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the battery state of + * the device. Will typically mirror battery state reported via intents such as + * {@link android.content.Intent#ACTION_BATTERY_LOW}. + * <p> + * Valid values: + * <UL> + * <LI>{@link #BATTERY_STATE_LOW}</LI> + * <LI>{@link #BATTERY_STATE_GOOD}</LI> + * <LI>{@link #BATTERY_STATE_CHARGING}</LI> + * </UL> + */ + public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; + + /** + * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via + * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the overall network + * coverage as it pertains to the current call. A {@link CallDiagnosticService} should signal + * poor coverage if the network coverage reaches a level where there is a high probability of + * the call dropping as a result. + * <p> + * Valid values: + * <UL> + * <LI>{@link #COVERAGE_POOR}</LI> + * <LI>{@link #COVERAGE_GOOD}</LI> + * </UL> + */ + public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "MESSAGE_", value = { + MESSAGE_CALL_NETWORK_TYPE, + MESSAGE_CALL_AUDIO_CODEC, + MESSAGE_DEVICE_BATTERY_STATE, + MESSAGE_DEVICE_NETWORK_COVERAGE + }) + public @interface MessageType {} + + /** + * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low. + */ + public static final int BATTERY_STATE_LOW = 1; + + /** + * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is not low. + */ + public static final int BATTERY_STATE_GOOD = 2; + + /** + * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is charging. + */ + public static final int BATTERY_STATE_CHARGING = 3; + + /** + * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is poor. + */ + public static final int COVERAGE_POOR = 1; + + /** + * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is good. + */ + public static final int COVERAGE_GOOD = 2; + + private Listener mListener; + private String mCallId; + + /** + * @hide + */ + public void setListener(@NonNull Listener listener) { + mListener = listener; + } + + /** + * Sets the call ID for this {@link CallDiagnostics}. + * @param callId + * @hide + */ + public void setCallId(@NonNull String callId) { + mCallId = callId; + } + + /** + * @return the Telecom call ID for this {@link CallDiagnostics}. + * @hide + */ + public @NonNull String getCallId() { + return mCallId; + } + + /** + * Telecom calls this method when the details of a call changes. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. + */ + public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details); + + /** + * The {@link CallDiagnosticService} implements this method to handle messages received via + * device to device communication. + * <p> + * See {@link #sendDeviceToDeviceMessage(int, int)} for background on device to device + * communication. + * <p> + * The underlying device to device communication protocol assumes that where there the two + * devices communicating are using a different version of the protocol, messages the recipient + * are not aware of are silently discarded. This means an older client talking to a new client + * will not receive newer messages and values sent by the new client. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. + */ + public abstract void onReceiveDeviceToDeviceMessage( + @MessageType int message, + int value); + + /** + * Sends a device to device message to the device on the other end of this call. + * <p> + * Device to device communication is an Android platform feature which supports low bandwidth + * communication between Android devices while they are in a call. The device to device + * communication leverages DTMF tones or RTP header extensions to pass messages. The + * messages are unacknowledged and sent in a best-effort manner. The protocols assume that the + * nature of the message are informational only and are used only to convey basic state + * information between devices. + * <p> + * Device to device messages are intentional simplifications of more rich indicators in the + * platform due to the extreme bandwidth constraints inherent with underlying device to device + * communication transports used by the telephony framework. Device to device communication is + * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets + * for a call, or using the DTMF digits A-D as a communication pathway. RTP header extension + * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for + * a message. Signalling requirements for DTMF digits place even more significant limitations + * on the amount of information which can be communicated during a call, offering only a few + * bits of potential information per message. The messages and values are constrained in order + * to meet the limited bandwidth inherent with DTMF signalling. + * <p> + * Allowed message types are: + * <ul> + * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI> + * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI> + * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI> + * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI> + * </ul> + * @param message The message type to send. + * @param value The message value corresponding to the type. + */ + public final void sendDeviceToDeviceMessage(int message, int value) { + if (mListener != null) { + mListener.onSendDeviceToDeviceMessage(this, message, value); + } + } + + /** + * Telecom calls this method when a GSM or CDMA call disconnects. + * The CallDiagnosticService can return a human readable disconnect message which will be passed + * to the Dialer app as the {@link DisconnectCause#getDescription()}. A dialer app typically + * shows this message at the termination of the call. If {@code null} is returned, the + * disconnect message generated by the telephony stack will be shown instead. + * <p> + * @param disconnectCause the disconnect cause for the call. + * @param preciseDisconnectCause the precise disconnect cause for the call. + * @return the disconnect message to use in place of the default Telephony message, or + * {@code null} if the default message will not be overridden. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. + */ + // TODO: Wire in Telephony support for this. + public abstract @Nullable CharSequence onCallDisconnected( + @Annotation.DisconnectCauses int disconnectCause, + @Annotation.PreciseDisconnectCauses int preciseDisconnectCause); + + /** + * Telecom calls this method when an IMS call disconnects and Telephony has already + * provided the disconnect reason info and disconnect message for the call. The + * {@link CallDiagnosticService} can intercept the raw IMS disconnect reason at this point and + * combine it with other call diagnostic information it is aware of to override the disconnect + * call message if desired. + * + * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection. + * @return A user-readable call disconnect message to use in place of the platform-generated + * disconnect message, or {@code null} if the disconnect message should not be overridden. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. + */ + // TODO: Wire in Telephony support for this. + public abstract @Nullable CharSequence onCallDisconnected( + @NonNull ImsReasonInfo disconnectReason); + + /** + * Telecom calls this method when a {@link CallQuality} report is received from the telephony + * stack for a call. + * @param callQuality The call quality report for this call. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. + */ + public abstract void onCallQualityReceived(@NonNull CallQuality callQuality); + + /** + * Signals the active default dialer app to display a call diagnostic message. This can be + * used to report problems encountered during the span of a call. + * <p> + * The {@link CallDiagnosticService} provides a unique client-specific identifier used to + * identify the specific diagnostic message type. + * <p> + * The {@link CallDiagnosticService} should call {@link #clearDiagnosticMessage(int)} when the + * diagnostic condition has cleared. + * @param messageId the unique message identifier. + * @param message a human-readable, localized message to be shown to the user indicating a + * call issue which has occurred, along with potential mitigating actions. + */ + public final void displayDiagnosticMessage(int messageId, @NonNull + CharSequence message) { + if (mListener != null) { + mListener.onDisplayDiagnosticMessage(this, messageId, message); + } + } + + /** + * Signals to the active default dialer that the diagnostic message previously signalled using + * {@link #displayDiagnosticMessage(int, CharSequence)} with the specified messageId is no + * longer applicable (e.g. service has improved, for example. + * @param messageId the message identifier for a message previously shown via + * {@link #displayDiagnosticMessage(int, CharSequence)}. + */ + public final void clearDiagnosticMessage(int messageId) { + if (mListener != null) { + mListener.onClearDiagnosticMessage(this, messageId); + } + } + + /** + * Called by the {@link CallDiagnosticService} to update the call details for this + * {@link CallDiagnostics} based on an update received from Telecom. + * @param newDetails the new call details. + * @hide + */ + public void handleCallUpdated(@NonNull Call.Details newDetails) { + onCallDetailsChanged(newDetails); + } +} diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 335857af8883..6dab6df22cf9 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -947,7 +947,7 @@ public abstract class Connection extends Conferenceable { * {@link CallDiagnosticService} implementation which is active. * <p> * Likewise, if a {@link CallDiagnosticService} sends a message using - * {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}, it will be routed to telephony + * {@link CallDiagnostics#sendDeviceToDeviceMessage(int, int)}, it will be routed to telephony * via {@link Connection#onCallEvent(String, Bundle)}. The telephony stack will relay the * message to the other device. * @hide @@ -960,7 +960,7 @@ public abstract class Connection extends Conferenceable { * Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device * message type. * - * See {@link DiagnosticCall} for more information. + * See {@link CallDiagnostics} for more information. * @hide */ @SystemApi @@ -971,7 +971,7 @@ public abstract class Connection extends Conferenceable { * Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device * message value. * <p> - * See {@link DiagnosticCall} for more information. + * See {@link CallDiagnostics} for more information. * @hide */ @SystemApi diff --git a/telecomm/java/android/telecom/DiagnosticCall.java b/telecomm/java/android/telecom/DiagnosticCall.java index af46b7759fb5..a6b7258052a4 100644 --- a/telecomm/java/android/telecom/DiagnosticCall.java +++ b/telecomm/java/android/telecom/DiagnosticCall.java @@ -16,338 +16,12 @@ package android.telecom; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; -import android.telephony.Annotation; -import android.telephony.CallQuality; -import android.telephony.ims.ImsReasonInfo; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.concurrent.Executor; /** - * A {@link DiagnosticCall} provides a way for a {@link CallDiagnosticService} to receive diagnostic - * information about a mobile call on the device. A {@link DiagnosticCall} is similar to a - * {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic - * and messaging capabilities not present on a {@link Call}. The {@link CallDiagnosticService} - * creates a {@link DiagnosticCall} for each {@link Call} on the device. This means that for each - * in progress call on the device, the {@link CallDiagnosticService} will create an instance of - * {@link DiagnosticCall}. - * <p> - * The {@link CallDiagnosticService} can generate mid-call diagnostic messages using the - * {@link #displayDiagnosticMessage(int, CharSequence)} API which provides the user with valuable - * information about conditions impacting their call and corrective actions. For example, if the - * {@link CallDiagnosticService} determines that conditions on the call are degrading, it can inform - * the user that the call may soon drop and that they can try using a different calling method - * (e.g. VOIP or WIFI). - * <h2>Threading Model</h2> - * All incoming IPC from Telecom in this class will use the same {@link Executor} as the - * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more - * information. + * @deprecated use {@link CallDiagnostics} instead. * @hide */ @SystemApi -public abstract class DiagnosticCall { - - /** - * @hide - */ - public interface Listener { - void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall, int message, int value); - void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId, - CharSequence message); - void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId); - } - - /** - * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via - * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type - * used for the current call. The call network type communicated here is an intentional - * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which - * removes some of the resolution inherent in those values; the - * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is - * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for - * efficiency of transport. For a discussion on the necessity of this simplification, see - * {@link #sendDeviceToDeviceMessage(int, int)}. - * <p> - * Valid values are below: - * <UL> - * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI> - * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI> - * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI> - * </UL> - */ - public static final int MESSAGE_CALL_NETWORK_TYPE = 1; - - /** - * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via - * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec - * used for the current call. - * <p> - * The audio codec communicated here is an intentional simplification of the - * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common - * variants of these audio codecs. Other variants of these codecs are reported as the next - * closest variant. For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec - * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}. - * For a discussion on the necessity of this simplification, see - * {@link #sendDeviceToDeviceMessage(int, int)}. - * <p> - * Valid values: - * <UL> - * <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI> - * <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI> - * <LI>{@link Connection#AUDIO_CODEC_AMR}</LI> - * </UL> - */ - public static final int MESSAGE_CALL_AUDIO_CODEC = 2; - - /** - * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via - * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the battery state of - * the device. Will typically mirror battery state reported via intents such as - * {@link android.content.Intent#ACTION_BATTERY_LOW}. - * <p> - * Valid values: - * <UL> - * <LI>{@link #BATTERY_STATE_LOW}</LI> - * <LI>{@link #BATTERY_STATE_GOOD}</LI> - * <LI>{@link #BATTERY_STATE_CHARGING}</LI> - * </UL> - */ - public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; - - /** - * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via - * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the overall network - * coverage as it pertains to the current call. A {@link CallDiagnosticService} should signal - * poor coverage if the network coverage reaches a level where there is a high probability of - * the call dropping as a result. - * <p> - * Valid values: - * <UL> - * <LI>{@link #COVERAGE_POOR}</LI> - * <LI>{@link #COVERAGE_GOOD}</LI> - * </UL> - */ - public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; - - /**@hide*/ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "MESSAGE_", value = { - MESSAGE_CALL_NETWORK_TYPE, - MESSAGE_CALL_AUDIO_CODEC, - MESSAGE_DEVICE_BATTERY_STATE, - MESSAGE_DEVICE_NETWORK_COVERAGE - }) - public @interface MessageType {} - - /** - * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low. - */ - public static final int BATTERY_STATE_LOW = 1; - - /** - * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is not low. - */ - public static final int BATTERY_STATE_GOOD = 2; - - /** - * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is charging. - */ - public static final int BATTERY_STATE_CHARGING = 3; - - /** - * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is poor. - */ - public static final int COVERAGE_POOR = 1; - - /** - * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is good. - */ - public static final int COVERAGE_GOOD = 2; - - private Listener mListener; - private String mCallId; - - /** - * @hide - */ - public void setListener(@NonNull Listener listener) { - mListener = listener; - } - - /** - * Sets the call ID for this {@link DiagnosticCall}. - * @param callId - * @hide - */ - public void setCallId(@NonNull String callId) { - mCallId = callId; - } - - /** - * @return the Telecom call ID for this {@link DiagnosticCall}. - * @hide - */ - public @NonNull String getCallId() { - return mCallId; - } - - /** - * Telecom calls this method when the details of a call changes. - * <p> - * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; - * see {@link CallDiagnosticService#getExecutor()} for more information. - */ - public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details); - - /** - * The {@link CallDiagnosticService} implements this method to handle messages received via - * device to device communication. - * <p> - * See {@link #sendDeviceToDeviceMessage(int, int)} for background on device to device - * communication. - * <p> - * The underlying device to device communication protocol assumes that where there the two - * devices communicating are using a different version of the protocol, messages the recipient - * are not aware of are silently discarded. This means an older client talking to a new client - * will not receive newer messages and values sent by the new client. - * <p> - * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; - * see {@link CallDiagnosticService#getExecutor()} for more information. - */ - public abstract void onReceiveDeviceToDeviceMessage( - @MessageType int message, - int value); - - /** - * Sends a device to device message to the device on the other end of this call. - * <p> - * Device to device communication is an Android platform feature which supports low bandwidth - * communication between Android devices while they are in a call. The device to device - * communication leverages DTMF tones or RTP header extensions to pass messages. The - * messages are unacknowledged and sent in a best-effort manner. The protocols assume that the - * nature of the message are informational only and are used only to convey basic state - * information between devices. - * <p> - * Device to device messages are intentional simplifications of more rich indicators in the - * platform due to the extreme bandwidth constraints inherent with underlying device to device - * communication transports used by the telephony framework. Device to device communication is - * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets - * for a call, or using the DTMF digits A-D as a communication pathway. RTP header extension - * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for - * a message. Signalling requirements for DTMF digits place even more significant limitations - * on the amount of information which can be communicated during a call, offering only a few - * bits of potential information per message. The messages and values are constrained in order - * to meet the limited bandwidth inherent with DTMF signalling. - * <p> - * Allowed message types are: - * <ul> - * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI> - * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI> - * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI> - * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI> - * </ul> - * @param message The message type to send. - * @param value The message value corresponding to the type. - */ - public final void sendDeviceToDeviceMessage(int message, int value) { - if (mListener != null) { - mListener.onSendDeviceToDeviceMessage(this, message, value); - } - } - - /** - * Telecom calls this method when a GSM or CDMA call disconnects. - * The CallDiagnosticService can return a human readable disconnect message which will be passed - * to the Dialer app as the {@link DisconnectCause#getDescription()}. A dialer app typically - * shows this message at the termination of the call. If {@code null} is returned, the - * disconnect message generated by the telephony stack will be shown instead. - * <p> - * @param disconnectCause the disconnect cause for the call. - * @param preciseDisconnectCause the precise disconnect cause for the call. - * @return the disconnect message to use in place of the default Telephony message, or - * {@code null} if the default message will not be overridden. - * <p> - * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; - * see {@link CallDiagnosticService#getExecutor()} for more information. - */ - // TODO: Wire in Telephony support for this. - public abstract @Nullable CharSequence onCallDisconnected( - @Annotation.DisconnectCauses int disconnectCause, - @Annotation.PreciseDisconnectCauses int preciseDisconnectCause); - - /** - * Telecom calls this method when an IMS call disconnects and Telephony has already - * provided the disconnect reason info and disconnect message for the call. The - * {@link CallDiagnosticService} can intercept the raw IMS disconnect reason at this point and - * combine it with other call diagnostic information it is aware of to override the disconnect - * call message if desired. - * - * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection. - * @return A user-readable call disconnect message to use in place of the platform-generated - * disconnect message, or {@code null} if the disconnect message should not be overridden. - * <p> - * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; - * see {@link CallDiagnosticService#getExecutor()} for more information. - */ - // TODO: Wire in Telephony support for this. - public abstract @Nullable CharSequence onCallDisconnected( - @NonNull ImsReasonInfo disconnectReason); - - /** - * Telecom calls this method when a {@link CallQuality} report is received from the telephony - * stack for a call. - * @param callQuality The call quality report for this call. - * <p> - * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; - * see {@link CallDiagnosticService#getExecutor()} for more information. - */ - public abstract void onCallQualityReceived(@NonNull CallQuality callQuality); - - /** - * Signals the active default dialer app to display a call diagnostic message. This can be - * used to report problems encountered during the span of a call. - * <p> - * The {@link CallDiagnosticService} provides a unique client-specific identifier used to - * identify the specific diagnostic message type. - * <p> - * The {@link CallDiagnosticService} should call {@link #clearDiagnosticMessage(int)} when the - * diagnostic condition has cleared. - * @param messageId the unique message identifier. - * @param message a human-readable, localized message to be shown to the user indicating a - * call issue which has occurred, along with potential mitigating actions. - */ - public final void displayDiagnosticMessage(int messageId, @NonNull - CharSequence message) { - if (mListener != null) { - mListener.onDisplayDiagnosticMessage(this, messageId, message); - } - } - - /** - * Signals to the active default dialer that the diagnostic message previously signalled using - * {@link #displayDiagnosticMessage(int, CharSequence)} with the specified messageId is no - * longer applicable (e.g. service has improved, for example. - * @param messageId the message identifier for a message previously shown via - * {@link #displayDiagnosticMessage(int, CharSequence)}. - */ - public final void clearDiagnosticMessage(int messageId) { - if (mListener != null) { - mListener.onClearDiagnosticMessage(this, messageId); - } - } - - /** - * Called by the {@link CallDiagnosticService} to update the call details for this - * {@link DiagnosticCall} based on an update received from Telecom. - * @param newDetails the new call details. - * @hide - */ - public void handleCallUpdated(@NonNull Call.Details newDetails) { - onCallDetailsChanged(newDetails); - } +public abstract class DiagnosticCall extends CallDiagnostics { } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 1a71f808daa7..45eafa45c78d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -33,6 +33,8 @@ import android.os.PersistableBundle; import android.os.RemoteException; import android.service.carrier.CarrierService; import android.telecom.TelecomManager; +import android.telephony.gba.TlsParams; +import android.telephony.gba.UaSecurityProtocolIdentifier; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsRegistrationAttributes; import android.telephony.ims.ImsSsData; @@ -3696,6 +3698,70 @@ public class CarrierConfigManager { */ public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool"; + /** + * Indicates that GBA_ME should be used for GBA authentication, as defined in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_ME = 1; + + /** + * Indicates that GBA_U should be used for GBA authentication, as defined in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_U = 2; + + /** + * Indicates that GBA_Digest should be used for GBA authentication, as defined + * in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_DIGEST = 3; + + /** + * An integer representing the GBA mode to use for requesting credentials + * via {@link TelephonyManager#bootstrapAuthenticationRequest}. + * + * One of {@link #GBA_ME}, {@link #GBA_U}, or {@link #GBA_DIGEST}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_MODE_INT = "gba_mode_int"; + + /** + * An integer representing the organization code to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code ORG_} constants in {@link UaSecurityProtocolIdentifier}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT = + "gba_ua_security_organization_int"; + + /** + * An integer representing the security protocol to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code UA_SECURITY_PROTOCOL_} constants in {@link UaSecurityProtocolIdentifier}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = + "gba_ua_security_protocol_int"; + + /** + * An integer representing the cipher suite to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code TLS_} constants in {@link android.telephony.gba.TlsParams}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = + "gba_ua_tls_cipher_suite_int"; /** * Configs used by ImsServiceEntitlement. @@ -4713,6 +4779,16 @@ public class CarrierConfigManager { public static final String KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY = "allowed_initial_attach_apn_types_string_array"; + /** + * Indicates whether or not the carrier will provision merged carrier Wi-Fi offload networks. + * Such networks are considered part of the core carrier network. + * + * This configuration will be use to gate whether such configurations are allowed to the carrier + * and correspondingly enable UI elements which are required for such configurations. + */ + public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = + "carrier_provisions_wifi_merged_networks_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -5255,6 +5331,13 @@ public class CarrierConfigManager { // Default wifi configurations. sDefaults.putAll(Wifi.getDefaults()); sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false); + sDefaults.putInt(KEY_GBA_MODE_INT, GBA_ME); + sDefaults.putInt(KEY_GBA_UA_SECURITY_ORGANIZATION_INT, + UaSecurityProtocolIdentifier.ORG_3GPP); + sDefaults.putInt(KEY_GBA_UA_SECURITY_PROTOCOL_INT, + UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT); + sDefaults.putInt(KEY_GBA_UA_TLS_CIPHER_SUITE_INT, TlsParams.TLS_NULL_WITH_NULL_NULL); + sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false); sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1)); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY, @@ -5276,6 +5359,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_HIDE_ENABLE_2G, false); sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY, new String[]{"ia", "default", "ims", "mms", "dun", "emergency"}); + sDefaults.putBoolean(KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL, false); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindow.aidl b/telephony/java/android/telephony/LinkCapacityEstimate.aidl index 546c406ef453..286f33fc9810 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindow.aidl +++ b/telephony/java/android/telephony/LinkCapacityEstimate.aidl @@ -14,16 +14,6 @@ * limitations under the License. */ -package com.android.wm.shell.startingsurface; +package android.telephony; -import com.android.wm.shell.startingsurface.IStartingWindowListener; - -/** - * Interface that is exposed to remote callers to manipulate starting windows. - */ -interface IStartingWindow { - /** - * Sets listener to get task launching callbacks. - */ - oneway void setStartingWindowListener(IStartingWindowListener listener) = 43; -} +parcelable LinkCapacityEstimate;
\ No newline at end of file diff --git a/telephony/java/android/telephony/LinkCapacityEstimate.java b/telephony/java/android/telephony/LinkCapacityEstimate.java new file mode 100644 index 000000000000..deeb80961c3c --- /dev/null +++ b/telephony/java/android/telephony/LinkCapacityEstimate.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Link Capacity Estimate from the modem + * @hide + */ +@SystemApi +public final class LinkCapacityEstimate implements Parcelable { + /** A value indicates that the capacity estimate is not available */ + public static final int INVALID = -1; + + /** + * LCE for the primary network + */ + public static final int LCE_TYPE_PRIMARY = 0; + + /** + * LCE for the secondary network + */ + public static final int LCE_TYPE_SECONDARY = 1; + + /** + * Combined LCE for primary network and secondary network reported by the legacy modem + */ + public static final int LCE_TYPE_COMBINED = 2; + + /** @hide */ + @IntDef(prefix = { "LCE_TYPE_" }, value = { + LCE_TYPE_PRIMARY, + LCE_TYPE_SECONDARY, + LCE_TYPE_COMBINED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LceType {} + + private final @LceType int mType; + + /** Downlink capacity estimate in kbps */ + private final int mDownlinkCapacityKbps; + + /** Uplink capacity estimate in kbps */ + private final int mUplinkCapacityKbps; + + /** + * Constructor for link capacity estimate + */ + public LinkCapacityEstimate(@LceType int type, + int downlinkCapacityKbps, int uplinkCapacityKbps) { + mDownlinkCapacityKbps = downlinkCapacityKbps; + mUplinkCapacityKbps = uplinkCapacityKbps; + mType = type; + } + + /** + * @hide + */ + public LinkCapacityEstimate(Parcel in) { + mDownlinkCapacityKbps = in.readInt(); + mUplinkCapacityKbps = in.readInt(); + mType = in.readInt(); + } + + /** + * Retrieves the type of LCE + * @return The type of link capacity estimate + */ + public @LceType int getType() { + return mType; + } + + /** + * Retrieves the downlink bandwidth in Kbps. + * This will be {@link #INVALID} if the network is not connected + * @return The estimated first hop downstream (network to device) bandwidth. + */ + public int getDownlinkCapacityKbps() { + return mDownlinkCapacityKbps; + } + + /** + * Retrieves the uplink bandwidth in Kbps. + * This will be {@link #INVALID} if the network is not connected + * + * @return The estimated first hop upstream (device to network) bandwidth. + */ + public int getUplinkCapacityKbps() { + return mUplinkCapacityKbps; + } + + @Override + public String toString() { + return new StringBuilder() + .append("{mType=") + .append(mType) + .append(", mDownlinkCapacityKbps=") + .append(mDownlinkCapacityKbps) + .append(", mUplinkCapacityKbps=") + .append(mUplinkCapacityKbps) + .append("}") + .toString(); + } + + /** + * {@link Parcelable#describeContents} + */ + public int describeContents() { + return 0; + } + + /** + * {@link Parcelable#writeToParcel} + * @hide + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mDownlinkCapacityKbps); + dest.writeInt(mUplinkCapacityKbps); + dest.writeInt(mType); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o == null || !(o instanceof LinkCapacityEstimate) || hashCode() != o.hashCode()) { + return false; + } + + if (this == o) { + return true; + } + + LinkCapacityEstimate that = (LinkCapacityEstimate) o; + return mDownlinkCapacityKbps == that.mDownlinkCapacityKbps + && mUplinkCapacityKbps == that.mUplinkCapacityKbps + && mType == that.mType; + } + + @Override + public int hashCode() { + return Objects.hash(mDownlinkCapacityKbps, mUplinkCapacityKbps, mType); + } + + public static final + @android.annotation.NonNull Parcelable.Creator<LinkCapacityEstimate> CREATOR = + new Parcelable.Creator() { + public LinkCapacityEstimate createFromParcel(Parcel in) { + return new LinkCapacityEstimate(in); + } + + public LinkCapacityEstimate[] newArray(int size) { + return new LinkCapacityEstimate[size]; + } + }; +} diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 3084b144c9b9..962200b82a81 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -9980,7 +9980,8 @@ public class TelephonyManager { } /** - * Sets the roaming mode for CDMA phone to the given mode {@code mode}. + * Sets the roaming mode for CDMA phone to the given mode {@code mode}. If the phone is not + * CDMA capable, this method does nothing. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} @@ -10003,6 +10004,7 @@ public class TelephonyManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaRoamingMode(@CdmaRoamingMode int mode) { + if (getPhoneType() != PHONE_TYPE_CDMA) return; try { ITelephony telephony = getITelephony(); if (telephony != null) { @@ -10083,7 +10085,8 @@ public class TelephonyManager { } /** - * Sets the subscription mode for CDMA phone to the given mode {@code mode}. + * Sets the subscription mode for CDMA phone to the given mode {@code mode}. If the phone is not + * CDMA capable, this method does nothing. * * @param mode CDMA subscription mode. * @throws SecurityException if the caller does not have the permission. @@ -10102,6 +10105,7 @@ public class TelephonyManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaSubscriptionMode(@CdmaSubscription int mode) { + if (getPhoneType() != PHONE_TYPE_CDMA) return; try { ITelephony telephony = getITelephony(); if (telephony != null) { @@ -14503,34 +14507,6 @@ public class TelephonyManager { } /** - * Get carrier bandwidth. In case of Dual connected network this will report - * bandwidth per primary and secondary network. It is possible that - * some modems may not fill secondary carrier bandwidth. - * @return CarrierBandwidth with bandwidth of both primary and secondary carrier. - * @throws IllegalStateException if the Telephony process is not currently available. - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - @NonNull - public CarrierBandwidth getCarrierBandwidth() { - try { - ITelephony service = getITelephony(); - if (service != null) { - return service.getCarrierBandwidth(getSubId()); - } else { - throw new IllegalStateException("telephony service is null."); - } - } catch (RemoteException ex) { - Log.e(TAG, "getCarrierBandwidth RemoteException", ex); - ex.rethrowFromSystemServer(); - } - - //Should not reach. Adding return statement to make compiler happy - return null; - } - - /** * Called when userActivity is signalled in the power manager. * This should only be called from system Uid. * @hide @@ -15478,7 +15454,9 @@ public class TelephonyManager { public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1; /** - * The unattended reboot was not prepared due to generic error. + * The unattended reboot was not prepared due to a non-recoverable error. After this error, + * the client that manages the unattended reboot should not try to invoke the API again + * until the next power cycle. * @hide */ @SystemApi diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index eb83ed0544f5..96af172e489e 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -31,7 +31,6 @@ import android.service.carrier.CarrierIdentifier; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telephony.CallForwardingInfo; -import android.telephony.CarrierBandwidth; import android.telephony.CarrierRestrictionRules; import android.telephony.CellIdentity; import android.telephony.CellInfo; @@ -2239,12 +2238,6 @@ interface ITelephony { boolean isNrDualConnectivityEnabled(int subId); /** - * Get carrier bandwidth per primary and secondary carrier - * @return CarrierBandwidth with bandwidth of both primary and secondary carrier. - */ - CarrierBandwidth getCarrierBandwidth(int subId); - - /** * Checks whether the device supports the given capability on the radio interface. * * @param capability the name of the capability 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 a8b7b057fe24..e6a4501fc529 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -35,15 +35,6 @@ fun FlickerTestParameter.navBarWindowIsAlwaysVisible() { } } -@JvmOverloads -fun FlickerTestParameter.visibleWindowsShownMoreThanOneConsecutiveEntry( - ignoreWindows: List<String> = emptyList() -) { - assertWm { - this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows) - } -} - fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper) { assertWm { this.showsAppWindowOnTop(testApp.getPackage()) @@ -184,15 +175,6 @@ fun FlickerTestParameter.statusBarLayerRotatesScales( } } -@JvmOverloads -fun FlickerTestParameter.visibleLayersShownMoreThanOneConsecutiveEntry( - ignoreLayers: List<String> = emptyList() -) { - assertLayers { - this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers) - } -} - fun FlickerTestParameter.appLayerReplacesWallpaperLayer(appName: String) { assertLayers { this.isVisible(WALLPAPER_TITLE) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index fef49d9433a8..d669a01b42f3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -36,8 +36,6 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer import com.android.server.wm.flicker.wallpaperWindowBecomesVisible import org.junit.Assume @@ -122,13 +120,17 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) @FlakyTest(bugId = 173689015) @Test open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry() + } } @FlakyTest(bugId = 173689015) @Test open fun visibleLayersShownMoreThanOneConsecutiveEntry() { - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry() + } } @Presubmit diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt index f7e749311442..fad25b4fa0b9 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt @@ -16,31 +16,13 @@ package com.android.server.wm.flicker.helpers -import android.os.RemoteException -import android.view.Surface import com.android.server.wm.flicker.Flicker +import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule /** * Changes the device [rotation] and wait for the rotation animation to complete * * @param rotation New device rotation */ -fun Flicker.setRotation(rotation: Int) { - try { - when (rotation) { - Surface.ROTATION_270 -> device.setOrientationRight() - Surface.ROTATION_90 -> device.setOrientationLeft() - Surface.ROTATION_0 -> device.setOrientationNatural() - else -> device.setOrientationNatural() - } - - wmHelper.waitForRotation(rotation) - wmHelper.waitForNavBarStatusBarVisible() - wmHelper.waitForAppTransitionIdle() - - // Ensure WindowManagerService wait until all animations have completed - instrumentation.uiAutomation.syncInputTransactions() - } catch (e: RemoteException) { - throw RuntimeException(e) - } -}
\ No newline at end of file +fun Flicker.setRotation(rotation: Int) = + ChangeDisplayOrientationRule.setRotation(rotation, instrumentation, wmHelper) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt index 47eaddfa1f3a..aad06ee94914 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt @@ -20,6 +20,7 @@ import android.app.Instrumentation import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.Surface +import android.view.WindowManagerPolicyConstants import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -31,8 +32,6 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -42,6 +41,7 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -99,8 +99,13 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter @Postsubmit @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE, + WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)) + } + } @Postsubmit @Test @@ -156,15 +161,23 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter @FlakyTest @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + fun visibleLayersShownMoreThanOneConsecutiveEntry() { + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry() + } + } companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(repetitions = 5) + .getConfigNonRotationTests( + repetitions = 5, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) + ) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt index 26afb794bb06..d08e7e2e5191 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -19,6 +19,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface +import android.view.WindowManagerPolicyConstants import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -30,8 +31,6 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -41,6 +40,7 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -99,8 +99,13 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @Presubmit @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE, + WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)) + } + } @Presubmit @Test @@ -163,14 +168,20 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @Test fun visibleLayersShownMoreThanOneConsecutiveEntry() { Assume.assumeFalse(testSpec.isRotated) - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME)) + } } @FlakyTest @Test fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() { Assume.assumeTrue(testSpec.isRotated) - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME)) + } } companion object { @@ -178,7 +189,12 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(repetitions = 5) + .getConfigNonRotationTests( + repetitions = 5, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) + ) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt index 2c4c627a444d..19dec7abee54 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt @@ -19,6 +19,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface +import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider @@ -33,12 +34,11 @@ import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -94,8 +94,13 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE, + WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)) + } + } @Presubmit @Test @@ -125,8 +130,11 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + fun visibleLayersShownMoreThanOneConsecutiveEntry() { + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry() + } + } @Presubmit @Test @@ -141,7 +149,12 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(repetitions = 5) + .getConfigNonRotationTests( + repetitions = 5, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) + ) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt index 2bcdcd9c8219..b214414ab7f1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -19,6 +19,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface +import android.view.WindowManagerPolicyConstants import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -38,8 +39,7 @@ import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -101,8 +101,13 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE, + WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)) + } + } @Presubmit @Test @@ -154,16 +159,25 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { @FlakyTest @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + fun visibleLayersShownMoreThanOneConsecutiveEntry() { + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME)) + } + } companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(repetitions = 5, - supportedRotations = listOf(Surface.ROTATION_0)) + .getConfigNonRotationTests( + repetitions = 5, + supportedRotations = listOf(Surface.ROTATION_0), + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) + ) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt index 6b2b930128fa..ab5e9b40463e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt @@ -19,6 +19,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface +import android.view.WindowManagerPolicyConstants import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -32,8 +33,6 @@ import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop import com.android.server.wm.flicker.dsl.FlickerBuilder @@ -155,21 +154,32 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { @FlakyTest @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + fun visibleLayersShownMoreThanOneConsecutiveEntry() { + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry() + } + } @FlakyTest @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry() + } + } companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(repetitions = 5, - supportedRotations = listOf(Surface.ROTATION_0)) + .getConfigNonRotationTests( + repetitions = 5, + supportedRotations = listOf(Surface.ROTATION_0), + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) + ) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt index 0cd5d7999a58..52a826655993 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt @@ -19,6 +19,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface +import android.view.WindowManagerPolicyConstants import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -36,8 +37,6 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation @@ -107,8 +106,11 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry() + } + } @Presubmit @Test @@ -176,15 +178,23 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { @FlakyTest @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + fun visibleLayersShownMoreThanOneConsecutiveEntry() { + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry() + } + } companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(repetitions = 1) + .getConfigNonRotationTests( + repetitions = 1, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) + ) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt index 56ed21b70b82..a9888b1876b7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -60,7 +60,8 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests() } } } 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 4a32a9eb3851..2e59dac30f44 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 @@ -22,8 +22,6 @@ import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.startRotation @@ -114,7 +112,7 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio @Test fun visibleWindowsShownMoreThanOneConsecutiveEntry_Flaky() { Assume.assumeTrue(testSpec.isRotated) - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + super.visibleWindowsShownMoreThanOneConsecutiveEntry() } @Presubmit @@ -128,7 +126,7 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio @Test fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() { Assume.assumeTrue(testSpec.isRotated) - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + super.visibleLayersShownMoreThanOneConsecutiveEntry() } companion object { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt index e9f053452589..5197f0ebc720 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt @@ -40,8 +40,6 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import org.junit.Assume import org.junit.Test @@ -128,13 +126,17 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { @Presubmit @Test open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry() + } } @FlakyTest @Test open fun visibleLayersShownMoreThanOneConsecutiveEntry() { - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry() + } } @Presubmit diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index a8b5ea1604ec..dee7e5945b1c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -72,7 +72,8 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index 9d78eb86539a..e914f64bb6ca 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -28,18 +28,14 @@ import com.android.server.wm.flicker.focusDoesNotChange import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import org.junit.Test abstract class RotationTransition(protected val testSpec: FlickerTestParameter) { @@ -50,12 +46,7 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) protected val endingPos get() = WindowUtils.getDisplayBounds(testSpec.config.endRotation) protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } setup { - test { - device.wakeUpAndGoToHomeScreen() - } eachRun { this.setRotation(testSpec.config.startRotation) } @@ -117,13 +108,18 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) @FlakyTest(bugId = 140855415) @Test - open fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + open fun visibleLayersShownMoreThanOneConsecutiveEntry() { + testSpec.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry() + } + } @Presubmit @Test open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + testSpec.assertWm { + this.visibleWindowsShownMoreThanOneConsecutiveEntry() + } } @Presubmit diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index fadd1eac14ef..58d8ee512dc2 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -202,6 +202,7 @@ import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkPolicyManager; import android.net.NetworkRequest; +import android.net.NetworkScore; import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkStackClient; @@ -1078,6 +1079,10 @@ public class ConnectivityServiceTest { public void triggerUnfulfillable(NetworkRequest r) { super.releaseRequestAsUnfulfillableByAnyFactory(r); } + + public void assertNoRequestChanged() { + assertNull(mRequestHistory.poll(0, r -> true)); + } } private Set<UidRange> uidRangesForUids(int... uids) { @@ -9160,7 +9165,8 @@ public class ConnectivityServiceTest { ConnectivityManager.getNetworkTypeName(TYPE_MOBILE), TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE)); return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(), - nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0, + nc, new NetworkScore.Builder().setLegacyInt(0).build(), + mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0, INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies()); } @@ -11119,11 +11125,99 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(cellCb); } + // Cannot be part of MockNetworkFactory since it requires method of the test. + private void expectNoRequestChanged(@NonNull MockNetworkFactory factory) { + waitForIdle(); + factory.assertNoRequestChanged(); + } + @Test - public void testRegisterBestMatchingNetworkCallback() throws Exception { - final NetworkRequest request = new NetworkRequest.Builder().build(); - assertThrows(UnsupportedOperationException.class, - () -> mCm.registerBestMatchingNetworkCallback(request, new NetworkCallback(), - mCsHandlerThread.getThreadHandler())); + public void testRegisterBestMatchingNetworkCallback_noIssueToFactory() throws Exception { + // Prepare mock mms factory. + final HandlerThread handlerThread = new HandlerThread("MockCellularFactory"); + handlerThread.start(); + NetworkCapabilities filter = new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_MMS); + final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "testFactory", filter, mCsHandlerThread); + testFactory.setScoreFilter(40); + + try { + // Register the factory and expect it will see default request, because all requests + // are sent to all factories. + testFactory.register(); + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(1); + // The factory won't try to start the network since the default request doesn't + // match the filter (no INTERNET capability). + assertFalse(testFactory.getMyStartRequested()); + + // Register callback for listening best matching network. Verify that the request won't + // be sent to factory. + final TestNetworkCallback bestMatchingCb = new TestNetworkCallback(); + mCm.registerBestMatchingNetworkCallback( + new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(), + bestMatchingCb, mCsHandlerThread.getThreadHandler()); + bestMatchingCb.assertNoCallback(); + expectNoRequestChanged(testFactory); + testFactory.assertRequestCountEquals(1); + assertFalse(testFactory.getMyStartRequested()); + + // Fire a normal mms request, verify the factory will only see the request. + final TestNetworkCallback mmsNetworkCallback = new TestNetworkCallback(); + final NetworkRequest mmsRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_MMS).build(); + mCm.requestNetwork(mmsRequest, mmsNetworkCallback); + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(2); + assertTrue(testFactory.getMyStartRequested()); + + // Unregister best matching callback, verify factory see no change. + mCm.unregisterNetworkCallback(bestMatchingCb); + expectNoRequestChanged(testFactory); + testFactory.assertRequestCountEquals(2); + assertTrue(testFactory.getMyStartRequested()); + } finally { + testFactory.terminate(); + } + } + + @Test + public void testRegisterBestMatchingNetworkCallback_trackBestNetwork() throws Exception { + final TestNetworkCallback bestMatchingCb = new TestNetworkCallback(); + mCm.registerBestMatchingNetworkCallback( + new NetworkRequest.Builder().addCapability(NET_CAPABILITY_TRUSTED).build(), + bestMatchingCb, mCsHandlerThread.getThreadHandler()); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + bestMatchingCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + + // Change something on cellular to trigger capabilities changed, since the callback + // only cares about the best network, verify it received nothing from cellular. + mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); + bestMatchingCb.assertNoCallback(); + + // Make cellular the best network again, verify the callback now tracks cellular. + mWiFiNetworkAgent.adjustScore(-50); + bestMatchingCb.expectAvailableCallbacksValidated(mCellNetworkAgent); + + // Make cellular temporary non-trusted, which will not satisfying the request. + // Verify the callback switch from/to the other network accordingly. + mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED); + bestMatchingCb.expectAvailableCallbacksValidated(mWiFiNetworkAgent); + mCellNetworkAgent.addCapability(NET_CAPABILITY_TRUSTED); + bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mCellNetworkAgent); + + // Verify the callback doesn't care about wifi disconnect. + mWiFiNetworkAgent.disconnect(); + bestMatchingCb.assertNoCallback(); + mCellNetworkAgent.disconnect(); + bestMatchingCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); } } diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java index 5760211d9a27..b7ece8f4c4c9 100644 --- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java @@ -312,14 +312,14 @@ public class DnsManagerTest { @Test public void testOverrideDefaultMode() throws Exception { // Hard-coded default is opportunistic mode. - final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mContentResolver); + final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mCtx); assertTrue(cfgAuto.useTls); assertEquals("", cfgAuto.hostname); assertEquals(new InetAddress[0], cfgAuto.ips); // Pretend a gservices push sets the default to "off". Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, "off"); - final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mContentResolver); + final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mCtx); assertFalse(cfgOff.useTls); assertEquals("", cfgOff.hostname); assertEquals(new InetAddress[0], cfgOff.ips); @@ -328,7 +328,7 @@ public class DnsManagerTest { Settings.Global.putString( mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com"); - final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mContentResolver); + final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mCtx); assertTrue(cfgStrict.useTls); assertEquals("strictmode.com", cfgStrict.hostname); assertEquals(new InetAddress[0], cfgStrict.ips); diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index 1c0ba4f8d8f5..ea2b362c537a 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -40,6 +40,7 @@ import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkProvider; +import android.net.NetworkScore; import android.os.Binder; import android.text.format.DateUtils; @@ -355,8 +356,9 @@ public class LingerMonitorTest { caps.addCapability(0); caps.addTransportType(transport); NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, - new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */, - mConnService, mNetd, mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), + new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(), + mCtx, null, new NetworkAgentConfig() /* config */, mConnService, mNetd, + mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), mQosCallbackTracker, new ConnectivityService.Dependencies()); nai.everValidated = true; return nai; diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index b8f7fbca3983..11fcea60d98d 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -1026,7 +1026,11 @@ public class VpnTest { .thenReturn(new Network[] { new Network(101) }); when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(), - anyInt(), any(), anyInt())).thenReturn(new Network(102)); + any(), any(), anyInt())).thenAnswer(invocation -> { + // The runner has registered an agent and is now ready. + legacyRunnerReady.open(); + return new Network(102); + }); final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile); final TestDeps deps = (TestDeps) vpn.mDeps; try { @@ -1048,7 +1052,7 @@ public class VpnTest { ArgumentCaptor<NetworkCapabilities> ncCaptor = ArgumentCaptor.forClass(NetworkCapabilities.class); verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(), - lpCaptor.capture(), ncCaptor.capture(), anyInt(), any(), anyInt()); + lpCaptor.capture(), ncCaptor.capture(), any(), any(), anyInt()); // In this test the expected address is always v4 so /32. // Note that the interface needs to be specified because RouteInfo objects stored in diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index 0e5f5e43f282..ca6448ca9b8c 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -188,7 +188,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection any(), lpCaptor.capture(), ncCaptor.capture(), - anyInt(), + any(), any(), anyInt()); verify(mIpSecSvc) |